├── .env ├── .gitignore ├── .npmignore ├── .travis.yml ├── bin └── mergePackageJson.js ├── bootstrap ├── browser-with-nlp.js ├── browser-with-parser.js ├── browser.js ├── node.js └── with-nlp.js ├── config └── extra-webpack.config.js ├── dist ├── newbot.min.js ├── newbot.min.js.gz ├── newbot.with-nlp.min.js ├── newbot.with-nlp.min.js.gz ├── newbot.with-parser.min.js └── newbot.with-parser.min.js.gz ├── docker ├── Dockerfile ├── readme.md └── sample │ └── bot │ ├── main.converse │ └── main.js ├── docs └── img │ └── logo-medium.png ├── functions └── request.js ├── index.js ├── loader ├── converse-loader.js ├── converse.js └── skill-loader.js ├── newbot.json ├── package-lock.json ├── package.json ├── packages ├── ask-lang │ ├── .gitignore │ ├── bot │ │ ├── main.converse │ │ ├── main.js │ │ ├── main.spec.js │ │ └── skills │ │ │ └── ask-lang │ │ │ ├── index.js │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ └── src │ │ │ ├── ask-lang.converse │ │ │ ├── ask-lang.js │ │ │ └── languages │ │ │ ├── en_EN.json │ │ │ ├── fr_FR.json │ │ │ └── index.js │ ├── emulator.bot │ └── index.js ├── dialogflow │ ├── .npmignore │ ├── bot │ │ ├── main.converse │ │ └── main.js │ ├── index.js │ ├── package-lock.json │ ├── package.json │ └── readme.md ├── express │ ├── .npmignore │ ├── connectors │ │ ├── alexa │ │ │ ├── alexa.js │ │ │ └── handlers.js │ │ ├── botbuilder-dialog.js │ │ ├── botbuilder.js │ │ ├── bottender.js │ │ ├── discord │ │ │ ├── handlers.js │ │ │ └── index.js │ │ ├── gactions.js │ │ └── twitter.js │ ├── output.js │ ├── package-lock.json │ ├── package.json │ ├── proactive.js │ ├── readme.md │ └── server.js ├── formats │ ├── .npmignore │ ├── browser │ │ └── index.js │ ├── index.js │ ├── node │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ ├── session │ │ ├── alexa.js │ │ ├── bottender.js │ │ ├── gactions.js │ │ └── twitter.js │ ├── src │ │ ├── formats │ │ │ ├── adaptive-card.js │ │ │ ├── alexa │ │ │ │ └── purchase │ │ │ │ │ ├── buy.js │ │ │ │ │ ├── cancel.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── schema.js │ │ │ ├── apl.js │ │ │ ├── buttons.js │ │ │ ├── carousel.js │ │ │ ├── contact.js │ │ │ ├── gif.js │ │ │ ├── hero-card.js │ │ │ ├── image.js │ │ │ ├── index.js │ │ │ ├── list.js │ │ │ ├── location.js │ │ │ ├── markdown.js │ │ │ ├── quick-replies.js │ │ │ ├── signin.js │ │ │ ├── special-replies.js │ │ │ ├── video.js │ │ │ └── webview.js │ │ └── utils.js │ └── tests │ │ ├── format.spec.js │ │ └── index.js ├── jest │ ├── index.js │ └── loader.js ├── prompts │ ├── .logs │ │ └── errors.log │ ├── .npmignore │ ├── bot │ │ ├── languages │ │ │ ├── en_EN.json │ │ │ ├── fr_FR.json │ │ │ └── index.js │ │ ├── main.converse │ │ ├── main.js │ │ ├── model │ │ │ └── model.nlp │ │ └── skills │ │ │ └── prompts │ │ │ ├── prompts.converse │ │ │ └── prompts.js │ ├── dist │ │ ├── browser │ │ │ ├── index.html │ │ │ ├── model │ │ │ │ └── model.nlp │ │ │ ├── skill.cjs.js │ │ │ └── skill.js │ │ └── node │ │ │ └── bot.js │ ├── index.js │ ├── newbot.config.js │ ├── package-lock.json │ ├── package.json │ └── readme.md ├── storage │ └── local │ │ ├── index.js │ │ ├── package-lock.json │ │ └── package.json ├── train │ ├── extract.js │ └── index.js ├── webpack │ └── index.js └── wit │ ├── .npmignore │ ├── bot │ ├── main.converse │ └── main.js │ ├── index.js │ ├── package-lock.json │ └── package.json ├── readme.md ├── sample ├── index.html ├── languages │ └── fr_FR.json └── skills │ ├── hey │ ├── hey.converse │ └── hey.js │ └── map │ ├── map.component.vue │ ├── map.converse │ └── map.js ├── src ├── api │ ├── array.js │ ├── debugger.js │ ├── eval.js │ ├── index.js │ ├── lang.js │ ├── users.js │ └── utils.js ├── converse.js ├── debug │ └── index.js ├── decorators │ ├── Action.js │ ├── Condition.js │ ├── Event.js │ ├── Intent.js │ ├── decorator.js │ ├── decorators.js │ └── index.js ├── error │ └── index.js ├── interpreter.js ├── nlp │ ├── index.js │ └── native.js ├── testing │ ├── assert.js │ ├── conversation │ │ ├── bot.js │ │ └── user.js │ ├── converse-testing.js │ └── user-testing.js ├── transpiler │ ├── grammar.js │ ├── lexer.js │ └── load.js ├── user.js └── utils │ ├── async.js │ ├── browser.js │ ├── fs.js │ ├── is-promise.js │ └── lodash.js ├── testing.js ├── tests ├── interpreter.spec.js ├── interpreter │ ├── action.converse │ ├── action.spec.js │ ├── array.converse │ ├── array.spec.js │ ├── condition.converse │ ├── condition.spec.js │ ├── expression.converse │ ├── expression.spec.js │ ├── format.converse │ ├── format.spec.js │ ├── function.converse │ ├── function.spec.js │ ├── functions.converse │ ├── functions.spec.js │ ├── group.converse │ ├── group.spec.js │ ├── input.converse │ ├── input.spec.js │ ├── intent.converse │ ├── intent.spec.js │ ├── lang.converse │ ├── lang.spec.js │ ├── network.converse │ ├── network.spec.js │ ├── nlp.converse │ ├── nlp.spec.js │ ├── object.converse │ ├── object.spec.js │ ├── output.converse │ ├── output.spec.js │ ├── request.converse │ ├── request.spec.js │ ├── trigger-event.converse │ ├── trigger-event.spec.js │ ├── variable.converse │ ├── variable.spec.js │ ├── while.converse │ ├── while.spec.js │ ├── wit.converse │ └── wit.spec.js ├── interpreter2 │ ├── block.spec.js │ ├── can-activated.spec.js │ ├── condition-decorator.spec.js │ ├── constants.spec.js │ ├── context.spec.js │ ├── else.spec.js │ ├── eval.spec.js │ ├── event.spec.js │ ├── format.spec.js │ ├── intent.spec.js │ ├── loop.spec.js │ ├── multi-skill.spec.js │ ├── native-nlp.spec.js │ ├── params.spec.js │ ├── pause.spec.js │ ├── regexp.spec.js │ ├── return.spec.js │ └── translate.spec.js ├── languages │ ├── en_EN.json │ └── fr_FR.json ├── model │ └── model.nlp ├── modules │ └── module.spec.js └── transpiler.spec.js ├── tsconfig.json ├── types ├── converse.ts ├── index.ts ├── skill.ts └── user.ts └── webpack.config.js /.env: -------------------------------------------------------------------------------- 1 | APITOKEN= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | _* 3 | .env.defaults 4 | .cache 5 | .build 6 | packages/**/dist 7 | !packages/prompts/dist 8 | newbot.config.js 9 | !packages/prompts/newbot.config.js 10 | editors -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .cache 2 | editors 3 | node_modules 4 | sample 5 | editors -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" -------------------------------------------------------------------------------- /bin/mergePackageJson.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const package = require('../package.json') 3 | const newbot = require('../newbot.json') 4 | 5 | package.version = newbot.version 6 | 7 | fs.writeFileSync(__dirname+ '/../package.json', JSON.stringify(package, null, 2)) -------------------------------------------------------------------------------- /bootstrap/browser-with-nlp.js: -------------------------------------------------------------------------------- 1 | import { Converse, NewBot } from './browser' 2 | import applyNlp from './with-nlp' 3 | 4 | applyNlp() 5 | 6 | export { 7 | Converse, 8 | NewBot 9 | } 10 | -------------------------------------------------------------------------------- /bootstrap/browser-with-parser.js: -------------------------------------------------------------------------------- 1 | import '../src/transpiler/load' 2 | import { Converse, NewBot } from './browser-with-nlp' 3 | 4 | export { Converse, NewBot } -------------------------------------------------------------------------------- /bootstrap/browser.js: -------------------------------------------------------------------------------- 1 | import Converse from '../src/converse' 2 | 3 | let NewBot 4 | 5 | if (typeof window !== 'undefined') { 6 | window.Converse = Converse 7 | window.NewBot = Converse 8 | } 9 | 10 | NewBot = Converse 11 | 12 | export { 13 | Converse, 14 | NewBot 15 | } 16 | -------------------------------------------------------------------------------- /bootstrap/node.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | 3 | require('../src/transpiler/load') 4 | const Converse = require('./with-nlp')(fs) 5 | const ConverseTesting = require('../src/testing/converse-testing') 6 | 7 | module.exports = { Converse, NewBot: Converse, ConverseTesting } -------------------------------------------------------------------------------- /bootstrap/with-nlp.js: -------------------------------------------------------------------------------- 1 | const { containerBootstrap } = require('@nlpjs/core') 2 | const { Nlp } = require('@nlpjs/nlp') 3 | const {BuiltinCompromise} = require('@nlpjs/builtin-compromise') 4 | const processNlp = require('../src/nlp/native') 5 | const Converse = require('../src/converse') 6 | const browser = require('../src/utils/browser') 7 | 8 | module.exports = (fs) => { 9 | Converse.nlpManager = async function(path, langs = []) { 10 | const container = await containerBootstrap() 11 | container.use(new Nlp({ forceNER: true, container })) 12 | const manager = container.get('nlp') 13 | 14 | for (let lang of langs) { 15 | container.use(lang) 16 | } 17 | 18 | if (path[0] == '{') { 19 | manager.import(path) 20 | } 21 | else if (browser.is()) { 22 | const model = await fetch(path).then(res => res.json()) 23 | manager.import(model) 24 | } 25 | else { 26 | const model = fs.readFileSync(path, 'utf-8') 27 | manager.import(model) 28 | } 29 | 30 | const builtin = new BuiltinCompromise() 31 | container.register('extract-builtin-??', builtin, true) 32 | 33 | return processNlp(manager) 34 | } 35 | return Converse 36 | } -------------------------------------------------------------------------------- /config/extra-webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Configuration used for third-party frameworks to read .converse files and newbot.config.js 3 | */ 4 | 5 | const path = require('path') 6 | require('../src/transpiler/load') 7 | 8 | module.exports = function(newbotConfig = {}) { 9 | let alias = {} 10 | 11 | if (newbotConfig.map) { 12 | for (let pkg in newbotConfig.map) { 13 | let platform = newbotConfig.map[pkg] 14 | if (typeof platform != 'string') platform = platform.browser 15 | alias[pkg] = platform 16 | } 17 | } 18 | 19 | return { 20 | module: { 21 | rules: [{ 22 | test: /\.converse$/, 23 | use: [{ 24 | loader: path.resolve(__dirname, '../loader/converse.js') 25 | }] 26 | }] 27 | }, 28 | resolve: { 29 | alias 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /dist/newbot.min.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newbotjs/newbot-framework/0da835e31f6889e0a907158dc76838e973074c8f/dist/newbot.min.js.gz -------------------------------------------------------------------------------- /dist/newbot.with-nlp.min.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newbotjs/newbot-framework/0da835e31f6889e0a907158dc76838e973074c8f/dist/newbot.with-nlp.min.js.gz -------------------------------------------------------------------------------- /dist/newbot.with-parser.min.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newbotjs/newbot-framework/0da835e31f6889e0a907158dc76838e973074c8f/dist/newbot.with-parser.min.js.gz -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 2 | 3 | RUN npm i -g newbot-cli 4 | 5 | EXPOSE 3000 6 | 7 | CMD newbot serve -------------------------------------------------------------------------------- /docker/readme.md: -------------------------------------------------------------------------------- 1 | docker run -------------------------------------------------------------------------------- /docker/sample/bot/main.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | > hello 4 | } -------------------------------------------------------------------------------- /docker/sample/bot/main.js: -------------------------------------------------------------------------------- 1 | import code from './main.converse' 2 | 3 | export default { 4 | code 5 | } -------------------------------------------------------------------------------- /docs/img/logo-medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newbotjs/newbot-framework/0da835e31f6889e0a907158dc76838e973074c8f/docs/img/logo-medium.png -------------------------------------------------------------------------------- /functions/request.js: -------------------------------------------------------------------------------- 1 | const _ = require('../src/utils/lodash') 2 | const request = require('axios') 3 | 4 | const Request = { 5 | 6 | $params: ['user'], 7 | 8 | $call(options, user) { 9 | return new Promise((resolve, reject) => { 10 | if (_.isString(options)) { 11 | options = { 12 | method: 'get', 13 | url: options 14 | } 15 | } 16 | request(options).then((response) => { 17 | const ret = { 18 | data: response.data, 19 | headers: response.headers, 20 | statusCode: response.status 21 | } 22 | user.setMagicVariable('response', ret) 23 | resolve(ret) 24 | }) 25 | }) 26 | }, 27 | 28 | $mock(ret, user) { 29 | user.setMagicVariable('response', ret) 30 | return ret 31 | } 32 | } 33 | 34 | 35 | module.exports = Request -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const NewBotTools = require('./bootstrap/node') 2 | module.exports = NewBotTools -------------------------------------------------------------------------------- /loader/converse-loader.js: -------------------------------------------------------------------------------- 1 | /* 2 | Text plugin 3 | */ 4 | exports.translate = function (load) { 5 | if (this.builder && this.transpiler) { 6 | load.metadata.format = 'esm'; 7 | return 'exp' + 'ort var __useDefault = ' + JSON.stringify(load.source) + '; exp' + 'ort default __useDefault;'; 8 | } 9 | 10 | load.metadata.format = 'amd'; 11 | return 'def' + 'ine(function() {\nreturn ' + JSON.stringify(load.source) + ';\n});'; 12 | } 13 | -------------------------------------------------------------------------------- /loader/converse.js: -------------------------------------------------------------------------------- 1 | const Transpiler = require('../src/transpiler/lexer') 2 | require('../src/transpiler/load') 3 | 4 | module.exports = function loader(code, map, meta) { 5 | const transpiler = new Transpiler(code) 6 | const obj = transpiler.run() 7 | const compiled = JSON.stringify(obj) 8 | code = code.replace(/`/g, '\\`') 9 | code = ` 10 | export default { 11 | code: \`${code}\`, 12 | compiled: ${compiled} 13 | }` 14 | return code 15 | }; -------------------------------------------------------------------------------- /loader/skill-loader.js: -------------------------------------------------------------------------------- 1 | exports.translate = function (load) { 2 | const converseUrlRegex = /file\s*:\s*(['"`](.*?)['"`])/g 3 | const relativePathRegex = /^\.{1,2}\//i 4 | const pathToApp = load.address 5 | .replace(this.baseURL, "") 6 | .replace(new RegExp("[^/]+$"), "") 7 | load.source = load.source.replace(/(import\s*.*?\s*from) \s*(['"`](.*?)['"`])/g, ($0, str, quotedUrl, path) => { 8 | if (path[0] == '.') { 9 | return $0 10 | } 11 | return `${str} '@node/${path}'` 12 | }) 13 | load.source = load.source.replace( 14 | converseUrlRegex, 15 | function replaceMatch($0, quotedUrl, url) { 16 | const absoluteUrl = relativePathRegex.test(url) ? 17 | (pathToApp + url) : 18 | url; 19 | return (`file: "${ absoluteUrl }"`) 20 | } 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /newbot.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.1.0" 3 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newbot", 3 | "version": "1.1.0", 4 | "description": "NewBot Framework is an open source Javascript framework for building chatbots", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npx mocha tests/*.spec.js tests/modules/*.spec.js tests/interpreter2/*.spec.js --timeout 0", 8 | "build": "node ./bin/mergePackageJson && webpack", 9 | "prepublish": "npm run build" 10 | }, 11 | "author": "Samuel Ronce", 12 | "license": "MIT", 13 | "dependencies": { 14 | "@nlpjs/builtin-compromise": "^4.22.0", 15 | "@nlpjs/core": "^4.22.0", 16 | "@nlpjs/nlp": "^4.22.0", 17 | "async-replace-promise": "^2.0.0", 18 | "axios": "^0.21.4", 19 | "callsite": "^1.0.0", 20 | "clean-webpack-plugin": "^4.0.0", 21 | "jsep": "^0.3.4", 22 | "languages-js": "^2.3.0", 23 | "lodash": "^4.17.15", 24 | "md5": "^2.2.1", 25 | "nodemon-webpack-plugin": "^4.5.2", 26 | "pegjs": "^0.10.0", 27 | "static-eval": "^2.0.3", 28 | "webpack-node-externals": "^3.0.0" 29 | }, 30 | "devDependencies": { 31 | "@nlpjs/lang-en": "^4.22.0", 32 | "@nlpjs/lang-fr": "^4.22.0", 33 | "chai": "^4.1.2", 34 | "compression-webpack-plugin": "^3.0.1", 35 | "mocha": "^6.2.2", 36 | "systemjs": "^0.21.4", 37 | "webpack": "^4.44.1", 38 | "webpack-cli": "^3.3.12" 39 | } 40 | } -------------------------------------------------------------------------------- /packages/ask-lang/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | creds.data -------------------------------------------------------------------------------- /packages/ask-lang/bot/main.converse: -------------------------------------------------------------------------------- 1 | @Intent(/start/i) 2 | start() { 3 | lang.ask(['fr_FR', 'en_EN']) 4 | > { lang.currentName() } 5 | } -------------------------------------------------------------------------------- /packages/ask-lang/bot/main.js: -------------------------------------------------------------------------------- 1 | import code from './main.converse' 2 | import lang from './skills/ask-lang' 3 | 4 | export default { 5 | code, 6 | skills: { 7 | lang 8 | } 9 | } -------------------------------------------------------------------------------- /packages/ask-lang/bot/main.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | ConverseTesting, 3 | user, 4 | bot 5 | } from '../../../testing' 6 | import assert from 'assert' 7 | import askLang from './main' 8 | 9 | describe('Ask Lang Skill Test', () => { 10 | let userConverse, converse 11 | 12 | beforeEach(() => { 13 | converse = new ConverseTesting(askLang) 14 | userConverse = converse.createUser({ 15 | session: { 16 | message: { 17 | source: 'website' 18 | } 19 | } 20 | }) 21 | }) 22 | 23 | it('Sample Test', () => { 24 | return userConverse 25 | .prompt('start', testing => { 26 | assert.deepStrictEqual(testing.output(0), { 27 | text: 'what is your language ?', 28 | actions: ['French', 'English'] 29 | }) 30 | }) 31 | .prompt('french', testing => { 32 | assert.equal(testing.output(0), 'Français') 33 | }) 34 | .end() 35 | }) 36 | }) -------------------------------------------------------------------------------- /packages/ask-lang/bot/skills/ask-lang/index.js: -------------------------------------------------------------------------------- 1 | import askLang from './src/ask-lang' 2 | export default askLang -------------------------------------------------------------------------------- /packages/ask-lang/bot/skills/ask-lang/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newbot-ask-lang", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npm test" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "MIT", 12 | "dependencies": { 13 | "newbot-formats": "*" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/ask-lang/bot/skills/ask-lang/src/ask-lang.converse: -------------------------------------------------------------------------------- 1 | ask(replies, options) { 2 | transform = _transformTo(replies) 3 | 4 | @Format('quickReplies', transform) 5 | > what language 6 | 7 | Prompt() 8 | 9 | lang = set(:text) 10 | 11 | if (unknown lang) { 12 | if (defined options.required) { 13 | > i do not understand 14 | ask(replies, options) 15 | } 16 | } 17 | 18 | return lang 19 | } -------------------------------------------------------------------------------- /packages/ask-lang/bot/skills/ask-lang/src/ask-lang.js: -------------------------------------------------------------------------------- 1 | import formats from 'newbot-formats' 2 | import languages from './languages' 3 | import code from './ask-lang.converse' 4 | 5 | const getInstanceLang = (fn) => { 6 | const { execution } = fn.converse 7 | return execution.converse.lang 8 | } 9 | 10 | export default { 11 | code, 12 | skills: { 13 | formats 14 | }, 15 | functions: { 16 | _transformTo(langs) { 17 | const langInstance = getInstanceLang(this) 18 | const { user } = this.converse 19 | const currentlang = user.getLang() 20 | return langs.map(lang => { 21 | if (currentlang) return langInstance.translate(lang, currentlang) 22 | return langInstance.translate(lang) 23 | }) 24 | }, 25 | set(text) { 26 | const { user } = this.converse 27 | const langInstance = getInstanceLang(this) 28 | const group = langInstance.getGroup('lang') 29 | let langFounded 30 | for (let langCode of group) { 31 | const valueCode = langInstance.translate(langCode) 32 | if (text.toLowerCase().includes(valueCode.toLowerCase())) { 33 | langFounded = langCode 34 | break 35 | } 36 | } 37 | if (langFounded) { 38 | user.setLang(langFounded) 39 | } 40 | return langFounded 41 | }, 42 | currentName() { 43 | const { user } = this.converse 44 | const langInstance = getInstanceLang(this) 45 | const lang = user.getLang() 46 | return langInstance.translate(lang, lang) 47 | } 48 | }, 49 | languages 50 | } -------------------------------------------------------------------------------- /packages/ask-lang/bot/skills/ask-lang/src/languages/en_EN.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "what language": "what is your language ?", 4 | "i do not understand": "i don't understand", 5 | "$lang": { 6 | "fr_FR": "french", 7 | "en_EN": "english", 8 | "es_ES": "spanish" 9 | } 10 | } 11 | ] -------------------------------------------------------------------------------- /packages/ask-lang/bot/skills/ask-lang/src/languages/fr_FR.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "what language": "quelle est votre langue ?", 4 | "i do not understand": "je n'ai pas compris", 5 | "$lang": { 6 | "fr_FR": "français", 7 | "en_EN": "anglais", 8 | "es_ES": "espagnol" 9 | } 10 | } 11 | ] -------------------------------------------------------------------------------- /packages/ask-lang/bot/skills/ask-lang/src/languages/index.js: -------------------------------------------------------------------------------- 1 | import en_EN from './en_EN.json' 2 | import fr_FR from './fr_FR.json' 3 | 4 | export default { 5 | packages: { en_EN, fr_FR } 6 | } -------------------------------------------------------------------------------- /packages/ask-lang/emulator.bot: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NewBot", 3 | "description": "", 4 | "secretKey": "", 5 | "services": [ 6 | { 7 | "appId": "", 8 | "id": "69908210-93d7-11e8-892c-f792db87b7d2", 9 | "type": "endpoint", 10 | "appPassword": "", 11 | "endpoint": "http://localhost:3000/emulator", 12 | "name": "http://localhost:3000/emulator" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/ask-lang/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./bot/main') -------------------------------------------------------------------------------- /packages/dialogflow/.npmignore: -------------------------------------------------------------------------------- 1 | .build 2 | bot -------------------------------------------------------------------------------- /packages/dialogflow/bot/main.converse: -------------------------------------------------------------------------------- 1 | @Intent('input.searchUser') 2 | searchUser() { 3 | > hello { :intent['given-name'].value } 4 | } 5 | 6 | @Intent('input.deleteUser') 7 | deleteUser() { 8 | > Je supprime { :intent['given-name'].value } 9 | } 10 | 11 | @Intent('dialogflow.error') 12 | error() { 13 | > { :intent.error } 14 | } -------------------------------------------------------------------------------- /packages/dialogflow/bot/main.js: -------------------------------------------------------------------------------- 1 | import dialogflowSkill from '../' 2 | import code from './main.converse' 3 | 4 | export default { 5 | code, 6 | skills: { 7 | dialogflowSkill: dialogflowSkill({ 8 | projectId: 'newagent-2431a', 9 | sessionId: 'quickstart-session-id', 10 | languageDefault: 'en-EN', 11 | credentials: '/home/samuel/www/.key/NewAgent-4cff0b6ec5a7.json' 12 | }) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/dialogflow/index.js: -------------------------------------------------------------------------------- 1 | const dialogflow = require('dialogflow') 2 | 3 | module.exports = function ({ 4 | projectId, 5 | languageDefault, 6 | credentials 7 | }) { 8 | if (credentials) { 9 | process.env.GOOGLE_APPLICATION_CREDENTIALS = credentials 10 | } 11 | 12 | const sessionClient = new dialogflow.SessionsClient() 13 | 14 | return { 15 | nlp: { 16 | async dialogflow(text, userId, converse) { 17 | 18 | try { 19 | const user = converse.users.get(userId) 20 | const languageCode = user.getLang() || languageDefault || 'en-EN' 21 | const sessionPath = sessionClient.sessionPath(projectId, userId) 22 | 23 | const request = { 24 | session: sessionPath, 25 | queryInput: { 26 | text: { 27 | text, 28 | languageCode: languageCode.replace('_', '-') 29 | } 30 | } 31 | } 32 | const responses = await sessionClient.detectIntent(request) 33 | 34 | const intents = {} 35 | for (let res of responses) { 36 | if (!res) continue 37 | const result = res.queryResult 38 | const { parameters, outputContexts } = result 39 | const retIntents = { 40 | response: result.fulfillmentText 41 | } 42 | const assignParams = (parameters) => { 43 | for (let key in parameters.fields) { 44 | retIntents[key] = { 45 | found: true, 46 | value: parameters.fields[key].stringValue 47 | } 48 | } 49 | } 50 | 51 | for (let context of outputContexts) { 52 | assignParams(context.parameters) 53 | } 54 | assignParams(parameters) 55 | 56 | intents[result.action] = () => { 57 | return retIntents 58 | } 59 | } 60 | return intents 61 | } 62 | catch (err) { 63 | console.error(err) 64 | return { 65 | 'dialogflow.error'() { 66 | return { 67 | error: err.message 68 | } 69 | } 70 | } 71 | } 72 | } 73 | }, 74 | shareNlp: true 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /packages/dialogflow/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newbot-dialogflow", 3 | "version": "1.1.1", 4 | "description": "DialogFlow in NewBot Framework", 5 | "main": "index.js", 6 | "scripts": {}, 7 | "author": "", 8 | "license": "ISC", 9 | "dependencies": { 10 | "dialogflow": "^0.6.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/dialogflow/readme.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Integrate the DialogFlow skill into NewBot 4 | 5 | # Installation 6 | 7 | Install with 8 | 9 | `npm install newbot-dialogflow` 10 | 11 | # Use 12 | 13 | 1. In your skill, import the package 14 | 2. Add the skill in the `skills` property and configure 15 | 16 | ```js 17 | import dialogflow from 'newbot-dialogflow' 18 | import code from './main.converse' 19 | 20 | export default { 21 | code, 22 | skills: { 23 | dialogflow: dialogflowSkill({ 24 | projectId: 'ENTER_PROJECT_ID_HERE', // https://dialogflow.com/docs/agents#settings, 25 | sessionId: 'quickstart-session-id', 26 | languageDefault: 'en-EN', // optionnal 27 | credentials: 'PATH_TO_JSON_FILE' // https://cloud.google.com/docs/authentication/production 28 | }) 29 | } 30 | } 31 | ``` 32 | 33 | # Use in ConverseScript 34 | 35 | In `main.converse` : 36 | 37 | ## Get Response 38 | 39 | ```ts 40 | @Intent('input.welcome') // action name 41 | welcome() { 42 | > { :intent.response } // response 43 | } 44 | ``` 45 | 46 | ## Get Response 47 | 48 | ```ts 49 | @Intent('input.welcome') // action name 50 | welcome() { 51 | > Hey { :intent.firstname.value } // display parameter "firstname" 52 | } 53 | ``` 54 | 55 | ## Get Error 56 | 57 | ```ts 58 | @Intent('dialogflow.error') 59 | error() { 60 | > { :intent.error } // display JS error 61 | } 62 | ``` -------------------------------------------------------------------------------- /packages/express/.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newbotjs/newbot-framework/0da835e31f6889e0a907158dc76838e973074c8f/packages/express/.npmignore -------------------------------------------------------------------------------- /packages/express/connectors/alexa/alexa.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | const Session = require('newbot-formats/session/alexa') 3 | const output = require('../../output') 4 | const handlers = require('./handlers') 5 | 6 | module.exports = function ({ 7 | app, 8 | settings, 9 | converse 10 | }) { 11 | const exec = (handlerInput, eventName, eventData = {}) => { 12 | const session = new Session(handlerInput) 13 | const { 14 | id: userId 15 | } = session.user 16 | const text = _.get(handlerInput.requestEnvelope, 'request.intent.slots.any.value') 17 | const _converse = global.converse || converse 18 | const _settings = output(session, settings) 19 | let p 20 | if (eventName) { 21 | p = _converse.event(eventName, eventData, [userId], _settings) 22 | } 23 | else { 24 | p = _converse.exec(text, userId, _settings) 25 | } 26 | return p.then(() => session.response) 27 | } 28 | 29 | const adapter = handlers(exec) 30 | 31 | app.post(settings.path || 'alexa', adapter.getRequestHandlers()) 32 | } 33 | -------------------------------------------------------------------------------- /packages/express/connectors/alexa/handlers.js: -------------------------------------------------------------------------------- 1 | const Alexa = require('ask-sdk-core') 2 | const { ExpressAdapter } = require('ask-sdk-express-adapter') 3 | const _ = require('lodash') 4 | 5 | const handlers = function(exec) { 6 | 7 | const LaunchRequestHandler = { 8 | canHandle(handlerInput) { 9 | return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest'; 10 | }, 11 | handle(handlerInput) { 12 | return exec(handlerInput, 'start') 13 | } 14 | } 15 | 16 | const NewBotHandler = { 17 | canHandle(handlerInput) { 18 | return handlerInput.requestEnvelope.request.type === 'IntentRequest' 19 | && handlerInput.requestEnvelope.request.intent.name === 'NewBotIntent'; 20 | }, 21 | handle(handlerInput) { 22 | return exec(handlerInput) 23 | } 24 | } 25 | 26 | const NewBotEventHandler = { 27 | canHandle(handlerInput) { 28 | const { type, reason } = handlerInput.requestEnvelope.request 29 | const intentName = _.get(handlerInput.requestEnvelope, 'request.intent.name') 30 | if ((type == 'IntentRequest' || type == 'SessionEndedRequest') && intentName && /^AMAZON/.test(intentName)) { 31 | handlerInput.eventName = intentName 32 | return true 33 | } 34 | else if (type == 'Connections.Response') { 35 | const { name, payload } = handlerInput.requestEnvelope.request 36 | handlerInput.eventName = `AMAZON.${name}` 37 | handlerInput.eventData = payload 38 | return true 39 | } 40 | else if (type == 'SessionEndedRequest' && reason == 'ERROR') { 41 | const { error } = handlerInput.requestEnvelope.request 42 | handlerInput.eventName = 'AMAZON.Error' 43 | handlerInput.eventData = error 44 | if (error.type == 'INVALID_RESPONSE') { 45 | console.log(`Warning! No answer is sent.`) 46 | } 47 | return true 48 | } 49 | return false 50 | }, 51 | handle(handlerInput) { 52 | return exec(handlerInput, handlerInput.eventName, handlerInput.eventData) 53 | } 54 | } 55 | 56 | const ErrorHandler = { 57 | canHandle() { 58 | return true; 59 | }, 60 | handle(handlerInput, error) { 61 | console.log(`~~~~ Error handled: ${error.stack}`); 62 | const speakOutput = `Sorry, I had trouble doing what you asked. Please try again.`; 63 | return handlerInput.responseBuilder 64 | .speak(speakOutput) 65 | .reprompt(speakOutput) 66 | .getResponse() 67 | } 68 | } 69 | 70 | const skillBuilder = Alexa.SkillBuilders.custom() 71 | .addRequestHandlers( 72 | LaunchRequestHandler, 73 | NewBotHandler, 74 | NewBotEventHandler 75 | ) 76 | .addErrorHandlers( 77 | ErrorHandler 78 | ) 79 | .withApiClient(new Alexa.DefaultApiClient()) 80 | 81 | const skill = skillBuilder.create() 82 | const adapter = new ExpressAdapter(skill, true, true) 83 | return adapter 84 | } 85 | 86 | module.exports = handlers -------------------------------------------------------------------------------- /packages/express/connectors/botbuilder-dialog.js: -------------------------------------------------------------------------------- 1 | const { 2 | ChatConnector, 3 | UniversalBot 4 | } = require('botbuilder') 5 | const output = require('../output') 6 | 7 | const connector = new ChatConnector() 8 | const bot = new UniversalBot(connector) 9 | 10 | let settings, converse 11 | 12 | bot.dialog('/', async (session, args = {}) => { 13 | const { 14 | event 15 | } = args 16 | const _converse = global.converse || converse 17 | const { 18 | text, 19 | user 20 | } = session.message 21 | 22 | const options = output(session, settings) 23 | 24 | if (event) { 25 | _converse.event(event.name, event.data, user.id, options).catch(console.log) 26 | } else { 27 | _converse.exec(text, user.id, options).catch(console.log) 28 | } 29 | }) 30 | 31 | module.exports = { 32 | init(_settings, _converse) { 33 | settings = _settings 34 | converse = _converse 35 | }, 36 | bot, 37 | connector 38 | } 39 | -------------------------------------------------------------------------------- /packages/express/connectors/botbuilder.js: -------------------------------------------------------------------------------- 1 | const { init, connector } = require('./botbuilder-dialog') 2 | 3 | module.exports = ({ 4 | settings, 5 | app, 6 | converse 7 | }) => { 8 | init(settings, converse) 9 | app.post(settings.path || '/botframework', connector.listen()) 10 | } 11 | -------------------------------------------------------------------------------- /packages/express/connectors/bottender.js: -------------------------------------------------------------------------------- 1 | const { 2 | MessengerBot, 3 | ViberBot, 4 | TelegramBot, 5 | LineBot, 6 | SlackBot, 7 | MessengerHandler 8 | } = require('bottender') 9 | const { registerRoutes } = require('bottender/express') 10 | const Session = require('newbot-formats/session/bottender') 11 | const output = require('../output') 12 | const _ = require('lodash') 13 | 14 | module.exports = ({ 15 | app, 16 | settings, 17 | converse, 18 | platform 19 | }) => { 20 | 21 | const event = async context => { 22 | const { 23 | text, 24 | isText 25 | } = context.event 26 | if (!isText) return 27 | const _converse = global.converse || converse 28 | const session = new Session(context) 29 | await _converse.exec(text, context.session.user.id, output(session, settings)) 30 | } 31 | 32 | const handler = new MessengerHandler() 33 | .onEvent(event) 34 | .onError(console.log) 35 | 36 | if (!settings.accessToken) return 37 | 38 | if (platform == 'messenger') { 39 | const messengerBot = new MessengerBot(settings).onEvent(handler) 40 | registerRoutes(app, messengerBot, { 41 | path: settings.path || '/messenger', 42 | verifyToken: settings.verifyToken 43 | }) 44 | } 45 | 46 | else if (platform == 'viber') { 47 | const viberBot = new ViberBot(settings).onEvent(handler) 48 | registerRoutes(app, viberBot, { 49 | path: settings.path || '/viber' 50 | }) 51 | } 52 | 53 | else if (platform == 'telegram') { 54 | const telegramBot = new TelegramBot(settings).onEvent(handler) 55 | registerRoutes(app, telegramBot, { 56 | path: settings.path || '/telegram' 57 | }) 58 | } 59 | 60 | else if (platform == 'line') { 61 | const lineBot = new LineBot(settings).onEvent(handler) 62 | registerRoutes(app, lineBot, { 63 | path: settings.path || '/line' 64 | }) 65 | } 66 | 67 | else if (platform == 'slack') { 68 | const slackBot = new SlackBot(settings).onEvent(handler) 69 | registerRoutes(app, slackBot, { 70 | path: settings.path || '/slack' 71 | }) 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /packages/express/connectors/discord/handlers.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newbotjs/newbot-framework/0da835e31f6889e0a907158dc76838e973074c8f/packages/express/connectors/discord/handlers.js -------------------------------------------------------------------------------- /packages/express/connectors/discord/index.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | const Session = require('newbot-formats/session/discord') 3 | const output = require('../../output') 4 | const handlers = require('./handlers') 5 | 6 | module.exports = function ({ 7 | app, 8 | settings, 9 | converse 10 | }) { 11 | const client = new Discord.Client() 12 | 13 | client.on('ready', () => { 14 | console.log(`Logged in as ${client.user.tag}!`) 15 | }); 16 | 17 | client.on('message', msg => { 18 | 19 | }) 20 | 21 | client.login(settings.accessToken) 22 | } 23 | -------------------------------------------------------------------------------- /packages/express/connectors/gactions.js: -------------------------------------------------------------------------------- 1 | const gactions = require('actions-on-google') 2 | const _ = require('lodash') 3 | const Session = require('newbot-formats/session/gactions') 4 | const output = require('../output') 5 | 6 | module.exports = function ({ 7 | app, 8 | converse, 9 | settings 10 | }) { 11 | const propClientId = 'platforms.gactions.signin.clientId' 12 | const clientId = _.get(settings, propClientId) 13 | 14 | const action = gactions.actionssdk({ 15 | clientId 16 | }) 17 | 18 | const handle = (conv, input, { 19 | type = 'exec', 20 | signin, 21 | userData 22 | } = {}) => { 23 | const _converse = global.converse || converse 24 | const session = new Session(gactions, conv) 25 | const userId = session.userId() 26 | const options = output(session, settings) 27 | 28 | if (type == 'exec') { 29 | return _converse.exec(input, userId, options) 30 | } 31 | 32 | return _converse.event(input, { 33 | profile: userData, 34 | signin 35 | }, userId, options) 36 | } 37 | 38 | const handleOption = (conv, params, option) => { 39 | return handle(conv, option) 40 | } 41 | 42 | const handleSignin = (conv, params, signin) => { 43 | const propName = 'platforms.gactions.signin.event' 44 | const eventName = _.get(settings, propName) 45 | if (!eventName) { 46 | throw '[Gactions] Please, add event name in "' + propName + '" property in "newbot.config.js"' 47 | } 48 | if (!clientId) { 49 | throw '[Gactions] Please, add client Id "' + propClientId + '" property in "newbot.config.js"' 50 | } 51 | return handle(conv, eventName, { 52 | type: 'event', 53 | signin, 54 | userData: conv.user.profile.payload 55 | }) 56 | } 57 | 58 | action.intent('actions.intent.MAIN', handle) 59 | action.intent('actions.intent.TEXT', handle) 60 | action.intent('actions.intent.OPTION', handleOption) 61 | action.intent('actions.intent.SIGN_IN', handleSignin) 62 | 63 | app.post(settings.path || '/gactions', action) 64 | } 65 | -------------------------------------------------------------------------------- /packages/express/connectors/twitter.js: -------------------------------------------------------------------------------- 1 | const { 2 | TwitterSession, 3 | CRCToken 4 | } = require('newbot-formats/session/twitter') 5 | const _ = require('lodash') 6 | const output = require('../output') 7 | 8 | module.exports = function ({ 9 | app, 10 | settings, 11 | converse 12 | }) { 13 | app.get(settings.path || '/twitter', (req, res) => { 14 | try { 15 | res.status(200).send(CRCToken(settings, req.query)) 16 | } catch (err) { 17 | res.status(400).send(err.message) 18 | } 19 | }) 20 | 21 | app.post(settings.path || '/twitter', async (req, res) => { 22 | const session = new TwitterSession(settings, req.body) 23 | const _converse = global.converse || converse 24 | 25 | if (session.userId) { 26 | await _converse.exec(session.text, session.userId, output(session, settings)) 27 | } 28 | 29 | res.status(204).end() 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /packages/express/output.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | 3 | module.exports = function(session, settings) { 4 | return options = _.merge({ 5 | output(str, next) { 6 | const ret = session.send(str) 7 | if (ret && ret.then) { 8 | ret.then(next) 9 | return 10 | } 11 | next() 12 | }, 13 | data: { 14 | session, 15 | webview: (url, data = {}) => { 16 | const btoa = str => Buffer.from(str).toString('base64') 17 | const params = encodeURIComponent(btoa(JSON.stringify(data))) 18 | url += `?data=${params}&webview=true` 19 | if (!url.startsWith('http')) { 20 | url = process.env.SERVER_URL + url 21 | } 22 | return url 23 | } 24 | } 25 | }, settings.output) 26 | } -------------------------------------------------------------------------------- /packages/express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newbot-express", 3 | "version": "0.4.0", 4 | "description": "Easily create a server with Express and NewBot Framework", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "keywords": [], 11 | "author": "NewBot", 12 | "license": "MIT", 13 | "dependencies": { 14 | "actions-on-google": "^2.8.0", 15 | "ask-sdk": "^2.7.0", 16 | "ask-sdk-core": "^2.7.0", 17 | "ask-sdk-express-adapter": "^2.0.0", 18 | "body-parser": "^1.19.0", 19 | "botbuilder": "^3.16.0", 20 | "bottender": "^0.15.17", 21 | "discord.js": "^11.5.1", 22 | "express": "^4.17.1", 23 | "lodash": "^4.17.14", 24 | "messaging-api-line": "^0.8.0-alpha.7", 25 | "messaging-api-messenger": "^0.7.16", 26 | "messaging-api-slack": "^0.7.11", 27 | "messaging-api-telegram": "^0.7.11", 28 | "messaging-api-viber": "^0.7.11", 29 | "newbot": "*", 30 | "newbot-formats": "*" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/express/proactive.js: -------------------------------------------------------------------------------- 1 | const { ViberClient } = require('messaging-api-viber') 2 | const { MessengerClient } = require('messaging-api-messenger') 3 | const { LineClient } = require('messaging-api-line') 4 | const { SlackWebhookClient } = require('messaging-api-slack') 5 | const { TelegramClient } = require('messaging-api-telegram') 6 | const Session = require('newbot-formats/session/bottender') 7 | const { bot } = require('./connectors/botbuilder-dialog') 8 | const _ = require('lodash') 9 | 10 | module.exports = function(settings, getSettings, converse) { 11 | const clients = {} 12 | 13 | const connect = (platformName, client) => { 14 | if (settings[platformName]) { 15 | const platformSetting = getSettings(platformName) 16 | if (!platformSetting.accessToken) { 17 | return false 18 | } 19 | clients[platformName] = client.connect(platformSetting.accessToken) 20 | } 21 | } 22 | 23 | connect('viber', ViberClient) 24 | connect('messenger', MessengerClient) 25 | connect('line', LineClient) 26 | connect('slack', SlackWebhookClient) 27 | connect('telegram', TelegramClient) 28 | 29 | return function(obj) { 30 | if (!obj.event) { 31 | throw 'You did not put the event property' 32 | } 33 | if (_.isString(obj.event)) { 34 | obj.event = { 35 | name: obj.event, 36 | data: {} 37 | } 38 | } 39 | if (!obj.platform) throw 'Please indicate the platform (messenger, telegram, viber, etc.)' 40 | if (!obj.userId) throw 'Please enter the user ID' 41 | if (!obj.event.name) throw 'Please indicate the name of the event to be triggered' 42 | 43 | if (obj.address) { 44 | bot.beginDialog(obj.address, '/', { 45 | event: obj.event 46 | }) 47 | return 48 | } 49 | 50 | const client = clients[obj.platform] 51 | if (!client) throw `This ${obj.platform} platform does not exist` 52 | 53 | const _converse = global.converse || converse 54 | 55 | const session = new Session(client, { 56 | userId: obj.userId 57 | }) 58 | 59 | _converse.event(obj.event.name, obj.event.data, obj.userId, _.merge({ 60 | output(str, next) { 61 | session.send(str) 62 | next() 63 | }, 64 | data: { 65 | session 66 | } 67 | }, settings.output)) 68 | } 69 | } -------------------------------------------------------------------------------- /packages/express/readme.md: -------------------------------------------------------------------------------- 1 | # Use 2 | 3 | ```js 4 | const express = require('express') 5 | const bot = require('newbot-express') 6 | 7 | const app = express() 8 | 9 | bot({ 10 | botframework: { 11 | path: '/emulator' 12 | }, 13 | output: { 14 | debug(type, val) { 15 | console.log(type, val) 16 | } 17 | } 18 | }, app) 19 | 20 | app.listen(5500) 21 | ``` -------------------------------------------------------------------------------- /packages/express/server.js: -------------------------------------------------------------------------------- 1 | const bodyParser = require('body-parser') 2 | const express = require('express') 3 | const socketIo = require('socket.io') 4 | const fs = require('fs') 5 | const _ = require('lodash') 6 | const { NewBot } = require('newbot') 7 | const botbuilder = require('./connectors/botbuilder') 8 | const gactions = require('./connectors/gactions') 9 | const bottender = require('./connectors/bottender') 10 | const twitter = require('./connectors/twitter') 11 | const alexa = require('./connectors/alexa/alexa') 12 | const proactive = require('./proactive') 13 | 14 | module.exports = function(settings, app, converse) { 15 | 16 | let mainSkill 17 | 18 | if (!converse) { 19 | const botPath = settings.botPath + '/dist/node/bot.js' 20 | if (!fs.existsSync(botPath)) { 21 | console.log('Bot skill not exists. use "newbot build" before') 22 | process.exit() 23 | } 24 | mainSkill = require(botPath) 25 | const converseOptions = {} 26 | if (settings.modelPath) { 27 | converseOptions.model = settings.modelPath 28 | } 29 | converse = new NewBot(mainSkill, converseOptions) 30 | } 31 | 32 | let config 33 | if (settings.botConfigFile && !_.isString(settings.botConfigFile)) { 34 | config = settings.botConfigFile 35 | } 36 | else { 37 | config = require(settings.botPath + '/' + (settings.botConfigFile || 'newbot.config')) 38 | } 39 | 40 | const getSettings = (platformName) => { 41 | const production = _.get(config, 'production.platforms.' + platformName) 42 | const dev = _.get(config, 'platforms.' + platformName) 43 | const obj = _.merge(process.env.NODE_ENV == 'production' ? production : dev, settings[platformName]) 44 | obj.output = settings.output || {} 45 | return obj 46 | } 47 | 48 | if (settings.alexa) { 49 | alexa({ 50 | settings: getSettings('alexa'), 51 | app, 52 | converse 53 | }) 54 | } 55 | 56 | app.use( 57 | bodyParser.json({ 58 | verify: (req, res, buf) => { 59 | req.rawBody = buf.toString(); 60 | }, 61 | }) 62 | ) 63 | 64 | if (settings.botframework) { 65 | botbuilder({ 66 | settings: getSettings('botframework'), 67 | app, 68 | converse 69 | }) 70 | } 71 | 72 | if (settings.gactions) { 73 | gactions({ 74 | settings: getSettings('gactions'), 75 | app, 76 | converse 77 | }) 78 | } 79 | 80 | if (settings.twitter) { 81 | twitter({ 82 | settings: getSettings('twitter'), 83 | app, 84 | converse 85 | }) 86 | }; 87 | 88 | 89 | ['messenger', 'viber', 'telegram', 'line', 'slack'].forEach(platform => { 90 | if (settings[platform]) { 91 | bottender({ 92 | settings: getSettings(platform), 93 | platform, 94 | app, 95 | converse 96 | }) 97 | } 98 | }) 99 | 100 | app.use('/webview', express.static(settings.botPath + '/webviews')) 101 | 102 | return { 103 | converse, 104 | config, 105 | proActiveEvent: proactive(settings, getSettings, converse) 106 | } 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /packages/formats/.npmignore: -------------------------------------------------------------------------------- 1 | .build 2 | bot -------------------------------------------------------------------------------- /packages/formats/browser/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | formats: { 3 | quickReplies(text, [actions]) { 4 | return { 5 | text, 6 | actions 7 | } 8 | }, 9 | email(text) { 10 | return { 11 | text, 12 | email: true 13 | } 14 | }, 15 | phone(text) { 16 | return { 17 | text, 18 | phone: true 19 | } 20 | }, 21 | image(text, url) { 22 | return { 23 | text, 24 | image: url 25 | } 26 | }, 27 | video(text, url) { 28 | return { 29 | text, 30 | video: url 31 | } 32 | }, 33 | buttons(text, [buttons]) { 34 | return { 35 | text, 36 | buttons 37 | } 38 | }, 39 | carousel(text, [cards, actions]) { 40 | return { 41 | text, 42 | cards, 43 | actions 44 | } 45 | } 46 | }, 47 | shareFormats: true 48 | } -------------------------------------------------------------------------------- /packages/formats/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./node') -------------------------------------------------------------------------------- /packages/formats/node/index.js: -------------------------------------------------------------------------------- 1 | const Utils = require('../src/utils') 2 | const formats = require('../src/formats') 3 | 4 | module.exports = (langDefault) => { 5 | 6 | Utils.defaultLanguage = langDefault 7 | 8 | return { 9 | shareFormats: true, 10 | formats, 11 | functions: { 12 | Typing: { 13 | $params: ['data'], 14 | $call({ session }) { 15 | if (!Utils.isWebSite(session)) { 16 | session.sendTyping() 17 | } 18 | } 19 | }, 20 | profile: { 21 | $params: ['data', 'user'], 22 | async $call({ session }, user) { 23 | let profile = {} 24 | if (Utils.isBottenderFacebook(session)) { 25 | const ret = await session.context.getUserProfile() 26 | profile = { 27 | name: ret.first_name, 28 | fullname: `${ret.first_name} ${ret.last_name}`, 29 | image: ret.profile_pic, 30 | lang: ret.locale, 31 | gender: ret.gender 32 | } 33 | } 34 | else if (Utils.isBottenderLine(session)) { 35 | const ret = await session.context.getUserProfile() 36 | profile = { 37 | name: ret.name, 38 | image: ret.pictureUrl 39 | } 40 | } 41 | else if (Utils.isBottenderViber(session)) { 42 | const ret = await session.context.getUserDetails() 43 | profile = { 44 | name: ret.name, 45 | image: ret.avatar, 46 | lang: ret.language, 47 | country: ret.country 48 | } 49 | } 50 | else if (Utils.isBottenderTelegram(session)) { 51 | const { from:ret } = session.context.event.inlineQuery() 52 | profile = { 53 | name: ret.first_name, 54 | fullname: `${ret.first_name} ${ret.last_name}`, 55 | lang: ret.language_code 56 | } 57 | } 58 | user.setMagicVariable('profile', profile) 59 | } 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /packages/formats/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newbot-formats", 3 | "version": "1.7.1", 4 | "description": "Use different formats for compatible chatbots on Messenger, Slack, Viber, Microsoft Bot Framework, Google Actions, etc.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha --timeout 0 tests/*.spec.js" 8 | }, 9 | "author": "NewBot", 10 | "license": "MIT", 11 | "dependencies": { 12 | "botbuilder": "^3.8.4", 13 | "lodash": "^4.17.11", 14 | "request": "^2.88.0", 15 | "request-promise": "^4.2.2", 16 | "uuid": "^3.3.2" 17 | }, 18 | "devDependencies": { 19 | "mocha": "^3.4.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/formats/session/alexa.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | 3 | class AlexaSession { 4 | constructor(handlerInput) { 5 | this.handlerInput = handlerInput 6 | this.dialogs = [] 7 | this.response = null 8 | this.platform = 'alexa' 9 | } 10 | 11 | supportsAPL() { 12 | const supportedInterfaces = this.handlerInput.requestEnvelope.context 13 | .System.device.supportedInterfaces 14 | const aplInterface = supportedInterfaces['Alexa.Presentation.APL'] 15 | return aplInterface != null && aplInterface !== undefined 16 | } 17 | 18 | async send(params) { 19 | try { 20 | let responseBuilder = this.handlerInput.responseBuilder 21 | 22 | if (_.isString(params)) { 23 | params = { 24 | text: params 25 | } 26 | } 27 | 28 | if (params.type == 'Alexa.Presentation.APL.RenderDocument' && this.supportsAPL()) { 29 | this.response.addDirective(params) 30 | } 31 | 32 | else if (params.type == 'Connections.SendRequest') { 33 | const productId = params.payload.InSkillProduct.productId 34 | if (!/\.product\./.test(productId)) { 35 | const locale = this.handlerInput.requestEnvelope.request.locale 36 | const monetizationClient = this.handlerInput.serviceClientFactory.getMonetizationServiceClient() 37 | const res = await monetizationClient.getInSkillProducts(locale) 38 | const product = _.find(res.inSkillProducts, p => p.referenceName == productId) 39 | if (!product) { 40 | throw 'Alexa Product not exists' 41 | } 42 | params.payload.InSkillProduct.productId = product.productId 43 | } 44 | 45 | this.response = responseBuilder 46 | .addDirective(params) 47 | } 48 | 49 | else { 50 | this.response = responseBuilder 51 | .speak(params.text) 52 | .reprompt(params.text) 53 | 54 | if (params.type == 'image') { 55 | const { 56 | smallImageUrl, 57 | largeImageUrl 58 | } = params.image 59 | 60 | this.response = this.response 61 | .withStandardCard(params.text, '', smallImageUrl, largeImageUrl) 62 | } 63 | } 64 | 65 | this.response = this.response.getResponse() 66 | 67 | } catch (err) { 68 | console.log(err) 69 | } 70 | } 71 | 72 | get user() { 73 | return { 74 | id: this.handlerInput.requestEnvelope.session.user.userId 75 | } 76 | } 77 | 78 | get message() { 79 | return { 80 | source: this.platform, 81 | agent: this.platform 82 | } 83 | } 84 | 85 | get source() { 86 | return this.message.source 87 | } 88 | } 89 | 90 | module.exports = AlexaSession 91 | -------------------------------------------------------------------------------- /packages/formats/session/bottender.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | 3 | class Session { 4 | constructor(context, { 5 | platform, 6 | userId 7 | } = {}) { 8 | this.context = context 9 | this.platform = platform 10 | this.userId = userId 11 | } 12 | 13 | send(methods) { 14 | if (!_.isArray(methods)) { 15 | methods = [methods] 16 | } 17 | for (let obj of methods) { 18 | if (_.isString(obj) || !obj.method) { 19 | if (obj.text) { 20 | obj = obj.text 21 | } 22 | obj = { 23 | method: 'sendText', 24 | params: [obj] 25 | } 26 | switch (this.platform) { 27 | case 'slack': 28 | obj.method = 'postMessage' 29 | break 30 | case 'telegram': 31 | obj.method = 'sendMessage' 32 | break; 33 | case 'line': 34 | obj.method = 'replyText' 35 | break; 36 | } 37 | } 38 | let params = obj.params 39 | if (this.userId) { 40 | params = [this.userId, ...params] 41 | } 42 | 43 | this.context[obj.method].apply(this.context, params) 44 | } 45 | } 46 | 47 | get message() { 48 | return { 49 | source: this.platform || this.context.platform, 50 | agent: 'bottender', 51 | user: this.user 52 | } 53 | } 54 | 55 | get user() { 56 | if (this.userId) { 57 | return { 58 | id: this.userId 59 | } 60 | } 61 | return this.context.session.user 62 | } 63 | 64 | get source() { 65 | return this.message.source 66 | } 67 | } 68 | 69 | module.exports = Session 70 | -------------------------------------------------------------------------------- /packages/formats/session/gactions.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | const uuid = require('uuid/v1') 3 | 4 | class Session { 5 | constructor(gactions, conv) { 6 | this.conv = conv 7 | this.gactions = gactions 8 | this.platform = 'gactions' 9 | } 10 | 11 | userId(id = uuid()) { 12 | let { userId } = this.conv.user.storage 13 | if (!userId) { 14 | userId = this.conv.user.storage.userId = id 15 | } 16 | return userId 17 | } 18 | 19 | send(params) { 20 | if (!_.isArray(params)) { 21 | params = [params] 22 | } 23 | params = params.map(obj => { 24 | if (obj.method) { 25 | obj.params = obj.params.map(param => { 26 | if (param.buttons) { 27 | param.buttons = new this.gactions.Button(param.buttons) 28 | } 29 | if (param.image) { 30 | param.image = new this.gactions.Image(param.image) 31 | } 32 | return param 33 | }) 34 | return new this.gactions[obj.method](...obj.params) 35 | } 36 | if (obj.text) { 37 | return obj.text 38 | } 39 | return obj 40 | }) 41 | this.conv.ask(...params) 42 | } 43 | 44 | get message() { 45 | return { 46 | source: this.platform, 47 | agent: this.platform, 48 | user: this.user 49 | } 50 | } 51 | 52 | get user() { 53 | return { 54 | id: this.conv.user.storage.userId 55 | } 56 | } 57 | 58 | get source() { 59 | return this.message.source 60 | } 61 | } 62 | 63 | module.exports = Session 64 | -------------------------------------------------------------------------------- /packages/formats/session/twitter.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto') 2 | const _ = require('lodash') 3 | const rp = require('request-promise') 4 | 5 | const uri = 'https://api.twitter.com/1.1' 6 | const uriUpload = 'https://upload.twitter.com/1.1' 7 | 8 | class TwitterSession { 9 | constructor(config, body) { 10 | this.oauth = { 11 | consumer_key: config.consumerKey, 12 | consumer_secret: config.consumerSecret, 13 | token: config.accessToken, 14 | token_secret: config.accessTokenSecret 15 | } 16 | this.appId = config.accessToken.split('-')[0] 17 | this.body = body 18 | this.platform = 'twitter' 19 | this._parse() 20 | } 21 | 22 | async send(messageData) { 23 | if (_.isString(messageData)) { 24 | messageData = { 25 | text: messageData 26 | } 27 | } 28 | if (messageData.attachment) { 29 | const { 30 | size, 31 | type, 32 | category, 33 | url 34 | } = messageData._data 35 | const { 36 | media_id: mediaId 37 | } = await rp.post({ 38 | url: `${uriUpload}/media/upload.json?command=INIT&total_bytes=${size}&media_types=${type}&media_category=${category}`, 39 | oauth: this.oauth, 40 | headers: { 41 | 'content-type': 'application/x-www-form-urlencoded' 42 | } 43 | }) 44 | 45 | const contentData = await rp({ 46 | url, 47 | encoding: 'binary' 48 | }) 49 | 50 | const ret = await rp.post({ 51 | url: `${uriUpload}/media/upload.json?command=APPEND&media_id=${mediaId}&media=${contentData}&segment_index=0`, 52 | oauth: this.oauth 53 | }) 54 | 55 | console.log(ret) 56 | } 57 | return await rp.post({ 58 | url: `${uri}/direct_messages/events/new.json`, 59 | oauth: this.oauth, 60 | headers: { 61 | 'content-type': 'application/json' 62 | }, 63 | json: true, 64 | body: { 65 | event: { 66 | type: 'message_create', 67 | message_create: { 68 | target: { 69 | recipient_id: this.userId 70 | }, 71 | message_data: messageData 72 | } 73 | } 74 | } 75 | }) 76 | } 77 | 78 | _parse() { 79 | 80 | console.log(JSON.stringify(this.body, null, 2)) 81 | 82 | const { 83 | direct_message_events: dms 84 | } = this.body 85 | 86 | if (!dms) { 87 | return 88 | } 89 | 90 | for (let dm of dms) { 91 | const { 92 | message_create: message 93 | } = dm 94 | const { 95 | message_data: messageData, 96 | sender_id: userId 97 | } = message 98 | 99 | if (this.appId == userId) continue 100 | 101 | this.text = messageData.text 102 | this.userId = userId 103 | } 104 | } 105 | 106 | get message() { 107 | return { 108 | source: this.platform, 109 | agent: this.platform 110 | } 111 | } 112 | 113 | get source() { 114 | return this.message.source 115 | } 116 | } 117 | 118 | const CRCToken = function (config, { 119 | crc_token: crcToken 120 | }) { 121 | if (crcToken) { 122 | const hash = crypto.createHmac('sha256', config.consumerSecret).update(crcToken).digest('base64') 123 | return { 124 | response_token: 'sha256=' + hash 125 | } 126 | } else { 127 | throw new Error('Error: crc_token missing from request.') 128 | } 129 | } 130 | 131 | module.exports = { 132 | TwitterSession, 133 | CRCToken 134 | } 135 | -------------------------------------------------------------------------------- /packages/formats/src/formats/adaptive-card.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const Utils = require('../utils') 3 | const _ = require('lodash') 4 | 5 | module.exports = async (text, [content], { 6 | session 7 | }) => { 8 | 9 | if (Utils.isBotBuilder(session)) { 10 | return new builder.Message(session) 11 | .text(text) 12 | .addAttachment({ 13 | content 14 | }) 15 | } 16 | 17 | return text 18 | } -------------------------------------------------------------------------------- /packages/formats/src/formats/alexa/purchase/buy.js: -------------------------------------------------------------------------------- 1 | const Utils = require('../../../utils') 2 | const schema = require('./schema') 3 | 4 | module.exports = (text, [productId], { 5 | session 6 | }) => { 7 | 8 | if (Utils.isAlexa(session)) { 9 | return schema('Buy', productId) 10 | } 11 | 12 | return text 13 | } -------------------------------------------------------------------------------- /packages/formats/src/formats/alexa/purchase/cancel.js: -------------------------------------------------------------------------------- 1 | const Utils = require('../../../utils') 2 | const schema = require('./schema') 3 | 4 | module.exports = (text, [productId], { 5 | session 6 | }) => { 7 | 8 | if (Utils.isAlexa(session)) { 9 | return schema('Cancel', productId) 10 | } 11 | 12 | return text 13 | } -------------------------------------------------------------------------------- /packages/formats/src/formats/alexa/purchase/index.js: -------------------------------------------------------------------------------- 1 | const buy = require('./buy') 2 | const cancel = require('./cancel') 3 | 4 | module.exports = { 5 | 'Amazon.Purchase.Buy': buy, 6 | 'Amazon.Purchase.Cancel': cancel 7 | } -------------------------------------------------------------------------------- /packages/formats/src/formats/alexa/purchase/schema.js: -------------------------------------------------------------------------------- 1 | module.exports = (name, productId) => { 2 | return { 3 | type: "Connections.SendRequest", 4 | name, 5 | payload: { 6 | InSkillProduct: { 7 | productId 8 | } 9 | }, 10 | token: "correlationToken" 11 | } 12 | } -------------------------------------------------------------------------------- /packages/formats/src/formats/apl.js: -------------------------------------------------------------------------------- 1 | module.exports = async (text, [document, datasources], { 2 | session 3 | }) => { 4 | 5 | if (Utils.isAlexa(session)) { 6 | return { 7 | type: 'Alexa.Presentation.APL.RenderDocument', 8 | version: '1.0', 9 | datasources, 10 | document 11 | } 12 | } 13 | 14 | return text 15 | } -------------------------------------------------------------------------------- /packages/formats/src/formats/buttons.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const Utils = require('../utils') 3 | const { 4 | heroCard 5 | } = require('./hero-card') 6 | 7 | function buttons(session, text, buttons, user) { 8 | const card = heroCard(session, { 9 | buttons 10 | }, user) 11 | 12 | if (Utils.isWebSite(session)) { 13 | return { 14 | text, 15 | buttons 16 | } 17 | } 18 | 19 | if (Utils.isGactions(session)) { 20 | return [ 21 | text, 22 | ...card.buttons.map(b => { 23 | let method, param 24 | switch (b.type) { 25 | case 'postback': 26 | method = 'Suggestions' 27 | param = b.msg || b.title 28 | break 29 | case 'url': 30 | case 'web_url': 31 | method = 'LinkOutSuggestion' 32 | param = { 33 | name: b.title, 34 | url: b.url 35 | } 36 | break 37 | } 38 | return { 39 | method, 40 | params: [param] 41 | } 42 | }) 43 | ] 44 | } 45 | 46 | if (Utils.isTwitter(session)) { 47 | return { 48 | text, 49 | ctas: card.buttons 50 | } 51 | } 52 | 53 | const facebook = { 54 | attachment: { 55 | type: 'template', 56 | payload: { 57 | template_type: 'button', 58 | text, 59 | buttons: card.buttons 60 | } 61 | } 62 | } 63 | 64 | if (Utils.isBotBuilderFacebook(session)) { 65 | return new builder.Message(session) 66 | .sourceEvent({ 67 | facebook 68 | }) 69 | } 70 | 71 | if (Utils.isBottenderFacebook(session)) { 72 | return { 73 | method: 'sendButtonTemplate', 74 | params: [ 75 | text, 76 | card.buttons 77 | ] 78 | } 79 | } 80 | 81 | if (Utils.isBottenderTelegram(session)) { 82 | return { 83 | method: 'sendMessage', 84 | params: [ 85 | text, 86 | { 87 | reply_markup: JSON.stringify({ 88 | inline_keyboard: card.buttons 89 | }) 90 | } 91 | ] 92 | } 93 | } 94 | 95 | if (Utils.isFacebook(session)) { 96 | return facebook 97 | } 98 | 99 | if (Utils.isBotBuilder(session)) { 100 | return new builder.Message(session) 101 | .text(text) 102 | .addAttachment(card) 103 | } 104 | 105 | return text 106 | } 107 | 108 | module.exports = { 109 | buttons, 110 | format(text, [_buttons], { 111 | session 112 | }, user) { 113 | return buttons(session, text, _buttons, user) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /packages/formats/src/formats/carousel.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const Utils = require('../utils') 3 | const { 4 | heroCard 5 | } = require('./hero-card') 6 | const { 7 | quickReplies 8 | } = require('./quick-replies') 9 | const _ = require('lodash') 10 | 11 | module.exports = (text, [cards, actions], { 12 | session 13 | }, user) => { 14 | 15 | actions = quickReplies(session, actions) 16 | 17 | if (!_.isArray(cards)) { 18 | return text 19 | } 20 | 21 | cards = cards.map(card => { 22 | return heroCard(session, card, user) 23 | }).slice(0, 10) 24 | 25 | if (Utils.isWebSite(session)) { 26 | return { 27 | text, 28 | cards, 29 | actions 30 | } 31 | } 32 | 33 | const facebook = { 34 | attachment: { 35 | type: "template", 36 | payload: { 37 | template_type: "generic", 38 | elements: cards 39 | } 40 | }, 41 | quick_replies: actions 42 | } 43 | 44 | if (Utils.isBotBuilderFacebook(session)) { 45 | return new builder.Message(session) 46 | .sourceEvent({ 47 | facebook 48 | }) 49 | } 50 | 51 | if (Utils.isGactions(session)) { 52 | return [ 53 | text, 54 | { 55 | method: 'Carousel', 56 | items: [ 57 | // todo 58 | ] 59 | } 60 | ] 61 | } 62 | 63 | if (Utils.isBottenderViber(session)) { 64 | return { 65 | method: 'sendCarouselContent', 66 | params: [{ 67 | Type: 'rich_media', 68 | ButtonsGroupColumns: 6, 69 | ButtonsGroupRows: 7, 70 | Buttons: _.flatten(cards) 71 | }] 72 | } 73 | } 74 | 75 | if (Utils.isBottenderLine(session)) { 76 | return { 77 | method: 'replyCarouselTemplate', 78 | params: [text, cards] 79 | } 80 | } 81 | 82 | if (Utils.isBottenderFacebook(session)) { 83 | return [ 84 | text, 85 | { 86 | method: 'sendGenericTemplate', 87 | params: [cards] 88 | } 89 | ] 90 | } 91 | 92 | if (Utils.isFacebook(session)) { 93 | return facebook 94 | } 95 | 96 | if (Utils.isBotBuilder(session)) { 97 | return new builder.Message(session) 98 | .attachmentLayout(builder.AttachmentLayout.carousel) 99 | .suggestedActions(builder.SuggestedActions.create(session, actions)) 100 | .attachments(cards) 101 | } 102 | 103 | return text 104 | } 105 | -------------------------------------------------------------------------------- /packages/formats/src/formats/contact.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const Utils = require('../utils') 3 | const _ = require('lodash') 4 | 5 | module.exports = (text, [phone, name], { 6 | session 7 | }) => { 8 | 9 | if (Utils.isWebSite(session)) { 10 | return { 11 | text, 12 | contact: { 13 | phone, 14 | name 15 | } 16 | } 17 | } 18 | 19 | if (Utils.isBottenderViber(session)) { 20 | return [ 21 | text, 22 | { 23 | method: 'sendContact', 24 | params: [{ 25 | name, 26 | phone_number: phone 27 | }] 28 | } 29 | ] 30 | } 31 | 32 | if (Utils.isBottenderTelegram(session)) { 33 | return [ 34 | text, 35 | { 36 | method: 'sendContact', 37 | params: [{ 38 | first_name: name, 39 | phone_number: phone 40 | }] 41 | } 42 | ] 43 | } 44 | 45 | if (Utils.isBottenderFacebook(session)) { 46 | return { 47 | method: 'sendTemplate', 48 | params: { 49 | template_type: 'button', 50 | text: name, 51 | buttons: [{ 52 | type: 'phone_number', 53 | title: 'Call', 54 | payload: phone 55 | }] 56 | } 57 | } 58 | } 59 | 60 | if (Utils.isBottenderLine(session)) { 61 | // todo 62 | return 63 | } 64 | 65 | if (Utils.isBotBuilder(session)) { 66 | // todo 67 | return 68 | } 69 | 70 | return text 71 | } 72 | -------------------------------------------------------------------------------- /packages/formats/src/formats/gif.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const Utils = require('../utils') 3 | 4 | module.exports = (text, [url], { 5 | session 6 | }) => { 7 | if (Utils.isWebSite(session)) { 8 | return { 9 | text, 10 | image 11 | } 12 | } 13 | if (Utils.isBotBuilder(session)) { 14 | return new builder.Message(session) 15 | .attachments([ 16 | new builder.AnimationCard(session) 17 | .text(text) 18 | .media([{ 19 | url 20 | }]) 21 | ]) 22 | } 23 | return text 24 | } 25 | -------------------------------------------------------------------------------- /packages/formats/src/formats/image.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const Utils = require('../utils') 3 | const _ = require('lodash') 4 | 5 | module.exports = async (text, [contentUrl, contentType, name, thumbnail], { 6 | session 7 | }) => { 8 | if (!name) { 9 | name = _.last(contentUrl.split('/')) 10 | } 11 | if (!contentType) { 12 | let ext = _.last(name.split('.')) 13 | ext = ext.toLowerCase() 14 | if (['gif', 'png', 'jpeg', 'jpg'].indexOf(ext)) { 15 | contentType = 'image/' + ext 16 | } 17 | } 18 | 19 | const facebook = { 20 | attachment: { 21 | type: 'image', 22 | payload: { 23 | url: contentUrl 24 | } 25 | } 26 | } 27 | 28 | if (Utils.isWebSite(session)) { 29 | return { 30 | text, 31 | image: contentUrl 32 | } 33 | } 34 | 35 | if (Utils.isTwitter(session)) { 36 | return { 37 | text, 38 | attachment: { 39 | type: 'media' 40 | }, 41 | _data: { 42 | url: contentUrl, 43 | size: await Utils.sizeFile(contentUrl), 44 | type: contentType, 45 | category: 'dm_image' 46 | } 47 | } 48 | } 49 | 50 | if (Utils.isGactions(session)) { 51 | return [ 52 | text, 53 | { 54 | method: 'Image', 55 | params: [{ 56 | url: contentUrl, 57 | alt: text 58 | }] 59 | } 60 | ] 61 | } 62 | 63 | if (Utils.isAlexa(session)) { 64 | return { 65 | type: 'image', 66 | text, 67 | image: { 68 | smallImageUrl: contentUrl, 69 | largeImageUrl: contentUrl 70 | } 71 | } 72 | } 73 | 74 | if (Utils.isBottenderViber(session)) { 75 | return { 76 | method: 'sendPicture', 77 | params: [{ 78 | text, 79 | media: contentUrl, 80 | thumbnail 81 | }] 82 | } 83 | } 84 | 85 | if (Utils.isBottenderTelegram(session)) { 86 | return [ 87 | text, 88 | { 89 | method: 'sendPhoto', 90 | params: [ 91 | contentUrl, 92 | { 93 | caption: name 94 | }] 95 | } 96 | ] 97 | } 98 | 99 | if (Utils.isBottenderFacebook(session)) { 100 | return [ 101 | text, 102 | { 103 | method: 'sendImage', 104 | params: [ 105 | contentUrl 106 | ] 107 | } 108 | ] 109 | } 110 | 111 | if (Utils.isBottenderLine(session)) { 112 | return { 113 | method: 'replyImage', 114 | params: [contentUrl] 115 | } 116 | } 117 | 118 | if (Utils.isFacebook(session) && !Utils.isBotBuilderFacebook(session)) { 119 | return facebook 120 | } 121 | 122 | if (Utils.isBotBuilder(session)) { 123 | return new builder.Message(session) 124 | .text(text) 125 | .addAttachment({ 126 | contentUrl, 127 | contentType, 128 | name 129 | }) 130 | } 131 | 132 | return text 133 | } 134 | -------------------------------------------------------------------------------- /packages/formats/src/formats/index.js: -------------------------------------------------------------------------------- 1 | const { format: quickReplies } = require('./quick-replies') 2 | const carousel = require('./carousel') 3 | const gif = require('./gif') 4 | const image = require('./image') 5 | const { format: buttons } = require('./buttons') 6 | const markdown = require('./markdown') 7 | const video = require('./video') 8 | const contact = require('./contact') 9 | const location = require('./location') 10 | const signin = require('./signin') 11 | const webview = require('./webview') 12 | const specialReplies = require('./special-replies') 13 | const purchase = require('./alexa/purchase') 14 | 15 | module.exports = { 16 | quickReplies, 17 | carousel, 18 | gif, 19 | buttons, 20 | markdown, 21 | image, 22 | video, 23 | contact, 24 | location, 25 | signin, 26 | webview, 27 | ...purchase, 28 | email: specialReplies('email'), 29 | phone: specialReplies('phone') 30 | } -------------------------------------------------------------------------------- /packages/formats/src/formats/list.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newbotjs/newbot-framework/0da835e31f6889e0a907158dc76838e973074c8f/packages/formats/src/formats/list.js -------------------------------------------------------------------------------- /packages/formats/src/formats/location.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const Utils = require('../utils') 3 | const _ = require('lodash') 4 | 5 | module.exports = (text, [latitude, longitude, { 6 | title, 7 | address 8 | } = {}], { 9 | session 10 | }) => { 11 | 12 | if (Utils.isWebSite(session)) { 13 | return { 14 | text, 15 | location: { 16 | latitude, 17 | longitude 18 | } 19 | } 20 | } 21 | 22 | if (Utils.isBottenderViber(session)) { 23 | return [ 24 | text, 25 | { 26 | method: 'sendLocation', 27 | params: [{ 28 | lat: latitude, 29 | lon: longitude 30 | }] 31 | } 32 | ] 33 | } 34 | 35 | if (Utils.isBottenderTelegram(session)) { 36 | return [ 37 | text, 38 | { 39 | method: 'sendLocation', 40 | params: [{ 41 | latitude, 42 | longitude 43 | }] 44 | } 45 | ] 46 | } 47 | 48 | if (Utils.isBottenderLine(session)) { 49 | return [ 50 | text, 51 | { 52 | method: 'replyLocation', 53 | params: [{ 54 | title, 55 | address, 56 | latitude, 57 | longitude 58 | }] 59 | } 60 | ] 61 | } 62 | 63 | if (Utils.isAlexa(session)) { 64 | return { 65 | type: 'AskForPermissionsConsent', 66 | permissions: [ 'read::alexa:device:all:address' ] 67 | } 68 | } 69 | 70 | if (Utils.isBottenderFacebook(session)) { 71 | // todo 72 | return 73 | } 74 | 75 | if (Utils.isBotBuilder(session)) { 76 | // todo 77 | return 78 | } 79 | 80 | return text 81 | } 82 | -------------------------------------------------------------------------------- /packages/formats/src/formats/markdown.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const Utils = require('../utils') 3 | 4 | module.exports = (text, params, { 5 | session 6 | }) => { 7 | if (Utils.isFacebook(session) && !Utils.isBotBuilderFacebook(session)) { 8 | return { 9 | text 10 | } 11 | } 12 | 13 | if (Utils.isWebSite(session)) { 14 | return { 15 | text, 16 | markdown: true 17 | } 18 | } 19 | 20 | if (Utils.isBotBuilder(session)) { 21 | return new builder.Message(session) 22 | .text(text) 23 | .textFormat('markdown') 24 | } 25 | return text 26 | } 27 | -------------------------------------------------------------------------------- /packages/formats/src/formats/signin.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const Utils = require('../utils') 3 | const _ = require('lodash') 4 | const { buttons } = require('./buttons') 5 | 6 | module.exports = (text, params = {}, { 7 | session 8 | }, user) => { 9 | 10 | if (Utils.isGactions(session)) { 11 | return [ 12 | { 13 | method: 'SignIn', 14 | params: [text] 15 | } 16 | ] 17 | } 18 | 19 | if (Utils.isAlexa(session)) { 20 | return { 21 | type: 'LinkAccount', 22 | text 23 | } 24 | } 25 | 26 | return buttons(session, text, [ 27 | { 28 | type: 'account_link', 29 | url: params.url 30 | } 31 | ], user) 32 | } -------------------------------------------------------------------------------- /packages/formats/src/formats/special-replies.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const browser = require('../../browser').default 3 | const Utils = require('../utils') 4 | 5 | module.exports = function (type) { 6 | return (text, params, { 7 | session 8 | }) => { 9 | 10 | if (Utils.isWebSite(session)) { 11 | return browser.formats[type](text) 12 | } 13 | 14 | let contentType 15 | switch (type) { 16 | case 'phone': 17 | contentType = 'user_phone_number' 18 | break 19 | case 'email': 20 | contentType = 'user_email' 21 | break 22 | } 23 | 24 | const facebook = { 25 | text, 26 | quick_replies: [{ 27 | content_type: contentType 28 | }] 29 | } 30 | 31 | if (Utils.isBotBuilderFacebook(session)) { 32 | return new builder.Message(session) 33 | .sourceEvent({ 34 | facebook 35 | }) 36 | } 37 | 38 | if (Utils.isBottenderFacebook(session)) { 39 | return { 40 | method: 'sendText', 41 | params: [ 42 | text, 43 | { 44 | quick_replies: facebook.quick_replies 45 | } 46 | ] 47 | } 48 | } 49 | 50 | if (Utils.isFacebook(session)) { 51 | return facebook 52 | } 53 | 54 | return text 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/formats/src/formats/video.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const Utils = require('../utils') 3 | const _ = require('lodash') 4 | const rp = require('request-promise') 5 | 6 | module.exports = async (text, [contentUrl, contentType, name, { 7 | thumbnail, 8 | duration, 9 | size 10 | } = {}], { 11 | session 12 | }, user) => { 13 | if (!name) { 14 | name = _.last(contentUrl.split('/')) 15 | } 16 | if (!contentType) { 17 | let ext = _.last(name.split('.')) 18 | ext = ext.toLowerCase() 19 | if (['mp4'].indexOf(ext)) { 20 | contentType = 'image/' + ext 21 | } 22 | } 23 | 24 | if (Utils.isWebSite(session)) { 25 | return { 26 | text, 27 | video: contentUrl 28 | } 29 | } 30 | 31 | 32 | if (Utils.isBottenderViber(session)) { 33 | const sizeFile = await Utils.sizeFile(contentUrl) 34 | return [ 35 | text, 36 | { 37 | method: 'sendVideo', 38 | params: [{ 39 | media: contentUrl, 40 | thumbnail, 41 | duration, 42 | size: size || sizeFile 43 | }] 44 | } 45 | ] 46 | } 47 | 48 | if (Utils.isGactions(session)) { 49 | return [ 50 | text, 51 | { 52 | method: 'BasicCard', 53 | params: [{ 54 | text: name, 55 | buttons: { 56 | title: Utils.getByLang({ 57 | fr_FR: 'Voir la vidéo', 58 | en_EN: 'View video' 59 | }, user, 'en_EN'), 60 | url: contentUrl 61 | } 62 | }] 63 | } 64 | ] 65 | } 66 | 67 | if (Utils.isBottenderLine(session)) { 68 | return [ 69 | text, 70 | { 71 | method: 'replyVideo', 72 | params: [contentUrl, thumbnail] 73 | } 74 | ] 75 | } 76 | 77 | if (Utils.isBottenderTelegram(session)) { 78 | return { 79 | method: 'sendVideo', 80 | params: [ 81 | contentUrl, 82 | { 83 | thumb: thumbnail, 84 | duration, 85 | caption: text 86 | } 87 | ] 88 | } 89 | } 90 | 91 | if (Utils.isBottenderFacebook(session)) { 92 | return [{ 93 | method: 'sendText', 94 | params: [ 95 | text 96 | ] 97 | }, { 98 | method: 'sendVideo', 99 | params: [ 100 | contentUrl 101 | ] 102 | }] 103 | } 104 | 105 | if (Utils.isBottenderLine(session)) { 106 | return { 107 | method: 'replyImage', 108 | params: [contentUrl] 109 | } 110 | } 111 | 112 | if (Utils.isFacebook(session) && !Utils.isBotBuilderFacebook(session)) { 113 | return facebook 114 | } 115 | 116 | if (Utils.isBotBuilder(session)) { 117 | return new builder.Message(session) 118 | .text(text) 119 | .addAttachment({ 120 | contentUrl, 121 | contentType, 122 | name 123 | }) 124 | } 125 | 126 | return text 127 | } 128 | -------------------------------------------------------------------------------- /packages/formats/src/formats/webview.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const Utils = require('../utils') 3 | const _ = require('lodash') 4 | const { buttons } = require('./buttons') 5 | 6 | module.exports = (text, [params = {}], { 7 | session 8 | }, user) => { 9 | let { url, data } = params 10 | if (!url.startsWith('http')) { 11 | const baseUrl = process.env.SERVER_URL 12 | url = (baseUrl ? baseUrl : '') + url 13 | } 14 | const btoa = str => Buffer.from(str).toString('base64') 15 | const pdata = encodeURIComponent(btoa(JSON.stringify(data || {}))) 16 | url += `?data=${pdata}&webview=true` 17 | if (Utils.isWebSite(session)) { 18 | return { 19 | text, 20 | webview: { 21 | url, 22 | height: params.height 23 | } 24 | } 25 | } 26 | return buttons(session, text, [ 27 | { 28 | type: 'webview', 29 | url, 30 | title: params.button, 31 | height: params.height 32 | } 33 | ], user) 34 | } -------------------------------------------------------------------------------- /packages/formats/src/utils.js: -------------------------------------------------------------------------------- 1 | const rp = require('request-promise') 2 | const LANGS_ID = ['fr_FR', 'en_EN'] 3 | 4 | class Utils { 5 | 6 | _is(session, platform) { 7 | const { source } = session.message 8 | return source === platform 9 | } 10 | 11 | isFacebook(session) { 12 | return this._is(session, 'facebook') || this._is(session, 'messenger') 13 | } 14 | 15 | isAlexa(session) { 16 | return this._is(session, 'alexa') 17 | } 18 | 19 | isGactions(session) { 20 | return this._is(session, 'gactions') 21 | } 22 | 23 | isViber(session) { 24 | return this._is(session, 'viber') 25 | } 26 | 27 | isSlack(session) { 28 | return this._is(session, 'slack') 29 | } 30 | 31 | isTelegram(session) { 32 | return this._is(session, 'telegram') 33 | } 34 | 35 | isTwitter(session) { 36 | return this._is(session, 'twitter') 37 | } 38 | 39 | isLine(session) { 40 | return this._is(session, 'line') 41 | } 42 | 43 | isBottender(session) { 44 | return session.message.agent === 'bottender' 45 | } 46 | 47 | isBotBuilder(session) { 48 | return session.message.agent === 'botbuilder' 49 | } 50 | 51 | isBotBuilderFacebook(session) { 52 | return this.isFacebook(session) && this.isBotBuilder(session) 53 | } 54 | 55 | /* Botrender */ 56 | isBottenderViber(session) { 57 | return this.isViber(session) && this.isBottender(session) 58 | } 59 | 60 | isBottenderFacebook(session) { 61 | return this.isFacebook(session) && this.isBottender(session) 62 | } 63 | 64 | isBottenderSlack(session) { 65 | return this.isSlack(session) && this.isBottender(session) 66 | } 67 | 68 | isBottenderTelegram(session) { 69 | return this.isTelegram(session) && this.isBottender(session) 70 | } 71 | 72 | isBottenderLine(session) { 73 | return this.isLine(session) && this.isBottender(session) 74 | } 75 | /** */ 76 | 77 | isWebSite(session) { 78 | return session.message.source === 'website' 79 | } 80 | 81 | getByLang(prop, user, _default) { 82 | const lang = user.getLang() 83 | if (prop && lang) { 84 | if (prop[lang]) { 85 | return prop[lang] 86 | } 87 | else if (prop[this.defaultLanguage]) { 88 | return prop[this.defaultLanguage] 89 | } 90 | else { 91 | const [lang] = Object.keys(prop) 92 | if (LANGS_ID.indexOf(lang) != -1) { 93 | return prop[lang] 94 | } 95 | } 96 | } 97 | if (_default) { 98 | return prop[_default] 99 | } 100 | return prop 101 | } 102 | 103 | toByLang(obj, user) { 104 | if (typeof obj == 'string') return obj 105 | for (let prop in obj) { 106 | obj[prop] = this.getByLang(obj[prop], user) 107 | } 108 | return obj 109 | } 110 | 111 | async sizeFile(url) { 112 | const { 113 | headers 114 | } = await rp({ 115 | url, 116 | method: 'GET', 117 | resolveWithFullResponse: true 118 | }) 119 | return headers['content-length'] 120 | } 121 | } 122 | 123 | module.exports = new Utils() -------------------------------------------------------------------------------- /packages/formats/tests/index.js: -------------------------------------------------------------------------------- 1 | const builder = require('botbuilder') 2 | const mainSkill = require('../index') 3 | const { ConverseTesting } = require('newbot/testing') 4 | 5 | module.exports = () => { 6 | const converse = new ConverseTesting(mainSkill('en_EN')) 7 | converse.testingWrapper((input, testingExec) => { 8 | connector = new builder.ConsoleConnector() 9 | bot = new builder.UniversalBot(connector, (session) => { 10 | const { text } = session.message 11 | testingExec(text, { data: { session } }) 12 | }) 13 | connector.processMessage(input) 14 | }) 15 | return converse 16 | } -------------------------------------------------------------------------------- /packages/jest/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | "\\.converse$": "/node_modules/newbot/packages/jest/loader.js", 4 | "^.+\\.js$": "babel-jest" 5 | }, 6 | moduleFileExtensions: [ 7 | "js" 8 | ] 9 | } -------------------------------------------------------------------------------- /packages/jest/loader.js: -------------------------------------------------------------------------------- 1 | const Transpiler = require('../../src/transpiler/lexer') 2 | require('../../src/transpiler/load') 3 | 4 | module.exports.process = function (code, filepath) { 5 | const transpiler = new Transpiler(code) 6 | const obj = transpiler.run() 7 | const compiled = JSON.stringify(obj) 8 | code = code.replace(/`/g, '\\`') 9 | code = ` 10 | module.exports = { 11 | code: \`${code}\`, 12 | compiled: ${compiled} 13 | }` 14 | return code 15 | } -------------------------------------------------------------------------------- /packages/prompts/.logs/errors.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newbotjs/newbot-framework/0da835e31f6889e0a907158dc76838e973074c8f/packages/prompts/.logs/errors.log -------------------------------------------------------------------------------- /packages/prompts/.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newbotjs/newbot-framework/0da835e31f6889e0a907158dc76838e973074c8f/packages/prompts/.npmignore -------------------------------------------------------------------------------- /packages/prompts/bot/languages/en_EN.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "what is your email": "what is your email?", 4 | "what is your phone": "what is your phone number?", 5 | "your email is invalid": "your email is invalid!", 6 | "your phone is invalid": "your phone number is invalid!", 7 | "give me date": "give me a date", 8 | "yes": "yes", 9 | "no": "no", 10 | "not understand confirmation": "i did not understand the confirmation", 11 | "number max": "Give a number less than %d", 12 | "number min": "Give a number greater than %d" 13 | } 14 | ] -------------------------------------------------------------------------------- /packages/prompts/bot/languages/fr_FR.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "what is your email": "quel est votre email ?", 4 | "what is your phone": "quel est votre numéro de téléphone ?", 5 | "your email is invalid": "votre email est invalide !", 6 | "your phone is invalid": "votre numéro de téléphone est invalide !", 7 | "give me date": "donnez-moi une date", 8 | "yes": "oui", 9 | "no": "non", 10 | "not understand confirmation": "je n'ai pas compris la confirmation", 11 | "number max": "Donnez-un nombre inférieur à %d", 12 | "number min": "Donnez-un nombre supérieur à %d" 13 | } 14 | ] -------------------------------------------------------------------------------- /packages/prompts/bot/languages/index.js: -------------------------------------------------------------------------------- 1 | import en_EN from './en_EN.json' 2 | import fr_FR from './fr_FR.json' 3 | 4 | export default { 5 | packages: { en_EN, fr_FR } 6 | } -------------------------------------------------------------------------------- /packages/prompts/bot/main.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | date = prompts.time('test') 4 | > { date } 5 | } -------------------------------------------------------------------------------- /packages/prompts/bot/main.js: -------------------------------------------------------------------------------- 1 | import code from './main.converse' 2 | import prompts from './skills/prompts/prompts' 3 | 4 | export default { 5 | code, 6 | skills: { 7 | prompts 8 | } 9 | } -------------------------------------------------------------------------------- /packages/prompts/bot/model/model.nlp: -------------------------------------------------------------------------------- 1 | { 2 | "settings": { 3 | "tag": "nlp", 4 | "threshold": 0.5, 5 | "autoLoad": true, 6 | "autoSave": false, 7 | "modelFileName": "model.nlp" 8 | }, 9 | "nluManager": { 10 | "settings": { 11 | "tag": "nlu-manager" 12 | }, 13 | "locales": [], 14 | "languageNames": {}, 15 | "domainManagers": {}, 16 | "intentDomains": {}, 17 | "extraSentences": [] 18 | }, 19 | "ner": { 20 | "settings": { 21 | "tag": "ner" 22 | }, 23 | "rules": {} 24 | }, 25 | "nlgManager": { 26 | "settings": { 27 | "tag": "nlg-manager" 28 | }, 29 | "responses": {} 30 | }, 31 | "actionManager": { 32 | "settings": { 33 | "tag": "action-manager" 34 | }, 35 | "actions": {} 36 | }, 37 | "slotManager": {} 38 | } -------------------------------------------------------------------------------- /packages/prompts/bot/skills/prompts/prompts.converse: -------------------------------------------------------------------------------- 1 | email() { 2 | @Format('email') 3 | > what is your email 4 | 5 | Prompt() 6 | 7 | bool = isEmail(:text) 8 | if (not bool) { 9 | > your email is invalid 10 | email() 11 | } 12 | return :text 13 | } 14 | 15 | phone() { 16 | @Format('phone') 17 | > what is your phone 18 | 19 | Prompt() 20 | 21 | phone = parsePhone(:text) 22 | 23 | if (unknown phone) { 24 | > your phone is invalid 25 | phone() 26 | } 27 | 28 | return phone 29 | } 30 | 31 | time(text) { 32 | @Format('date') 33 | > { text } 34 | 35 | Prompt() 36 | 37 | value = parseDate(:text) 38 | 39 | if (not value) { 40 | > give me date 41 | time(text) 42 | } 43 | 44 | return value 45 | } 46 | 47 | confirm(text) { 48 | @Format('quickReplies', ['#yes', '#no']) 49 | > { text } 50 | 51 | Prompt() 52 | 53 | bool = parseChoice(:text) 54 | 55 | if (unknown bool) { 56 | > not understand confirmation 57 | confirm(text) 58 | } 59 | 60 | return bool 61 | } 62 | 63 | number(text, options) { 64 | min = options.min 65 | max = options.max 66 | 67 | @Format('number', min, max) 68 | > { text } 69 | 70 | Prompt() 71 | 72 | number = parseNumber(:text) 73 | 74 | if (unknown number) { 75 | > not understand confirmation 76 | number(text, options) 77 | } 78 | else { 79 | if (defined min && number < min) { 80 | > number min [min] 81 | number(text, options) 82 | } 83 | else if (defined max && number > max) { 84 | > number max [max] 85 | number(text, options) 86 | } 87 | } 88 | 89 | return number 90 | } 91 | 92 | -------------------------------------------------------------------------------- /packages/prompts/bot/skills/prompts/prompts.js: -------------------------------------------------------------------------------- 1 | import isEmail from 'validator/lib/isEmail' 2 | import formats from 'newbot-formats' 3 | import chrono from 'chrono-node' 4 | import ChoiceRecognizers from '@microsoft/recognizers-text-choice' 5 | import NumberRecognizers from '@microsoft/recognizers-text-number' 6 | import SequenceRecognizers from '@microsoft/recognizers-text-sequence' 7 | import code from './prompts.converse' 8 | import languages from '../../languages' 9 | 10 | const getlang = function (params) { 11 | const { 12 | user 13 | } = params 14 | let lang = user.getLang() 15 | let langName 16 | if (lang) { 17 | lang = lang.split('_')[0] 18 | } else { 19 | lang = 'en' 20 | } 21 | if (!chrono[lang]) { 22 | lang = 'en' 23 | } 24 | switch (lang) { 25 | case 'en': 26 | langName = 'English' 27 | break; 28 | case 'fr': 29 | langName = 'French' 30 | break 31 | } 32 | return { 33 | langId: lang, 34 | langName 35 | } 36 | } 37 | 38 | const commonParse = function (type, text, converse) { 39 | let fn 40 | const { 41 | langName 42 | } = getlang(converse) 43 | switch (type) { 44 | case 'number': 45 | fn = NumberRecognizers.recognizeNumber 46 | break 47 | case 'bool': 48 | fn = ChoiceRecognizers.recognizeBoolean 49 | break 50 | case 'phone': 51 | fn = SequenceRecognizers.recognizePhoneNumber 52 | break 53 | } 54 | const ret = fn(text, ChoiceRecognizers.Culture[langName]) 55 | if (ret.length > 0) { 56 | return ret[0].resolution.value 57 | } 58 | return null 59 | } 60 | 61 | const commonFormat = function (text, obj, { 62 | session 63 | }) { 64 | const isWebSite = session.message.source === 'website' 65 | if (isWebSite) { 66 | return { 67 | text, 68 | ...obj 69 | } 70 | } 71 | return text 72 | } 73 | 74 | export default { 75 | code, 76 | languages, 77 | functions: { 78 | isEmail, 79 | parseDate(text) { 80 | const { 81 | langId 82 | } = getlang(this.converse) 83 | return chrono[langId].parseDate(text) 84 | }, 85 | parseChoice(text) { 86 | return commonParse('bool', text, this.converse) 87 | }, 88 | parseNumber(text) { 89 | return commonParse('number', text, this.converse) 90 | }, 91 | parsePhone(text) { 92 | return commonParse('phone', text, this.converse) 93 | } 94 | }, 95 | skills: { 96 | formats 97 | }, 98 | formats: { 99 | date(text, params, data) { 100 | return commonFormat(text, { 101 | date: true 102 | }, data) 103 | }, 104 | number(text, [min, max], data) { 105 | return commonFormat(text, { 106 | number: { 107 | min, 108 | max 109 | } 110 | }, data) 111 | }, 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /packages/prompts/dist/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |

 7 | 
8 | -------------------------------------------------------------------------------- /packages/prompts/dist/browser/model/model.nlp: -------------------------------------------------------------------------------- 1 | { 2 | "settings": { 3 | "tag": "nlp", 4 | "threshold": 0.5, 5 | "autoLoad": true, 6 | "autoSave": false, 7 | "modelFileName": "model.nlp" 8 | }, 9 | "nluManager": { 10 | "settings": { 11 | "tag": "nlu-manager" 12 | }, 13 | "locales": [], 14 | "languageNames": {}, 15 | "domainManagers": {}, 16 | "intentDomains": {}, 17 | "extraSentences": [] 18 | }, 19 | "ner": { 20 | "settings": { 21 | "tag": "ner" 22 | }, 23 | "rules": {} 24 | }, 25 | "nlgManager": { 26 | "settings": { 27 | "tag": "nlg-manager" 28 | }, 29 | "responses": {} 30 | }, 31 | "actionManager": { 32 | "settings": { 33 | "tag": "action-manager" 34 | }, 35 | "actions": {} 36 | }, 37 | "slotManager": {} 38 | } -------------------------------------------------------------------------------- /packages/prompts/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./bot/skills/prompts/prompts') -------------------------------------------------------------------------------- /packages/prompts/newbot.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | map: { 3 | 'newbot-formats': { 4 | node: 'newbot-formats/node', 5 | browser: 'newbot-formats/browser' 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /packages/prompts/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newbot-prompts", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@microsoft/recognizers-text": { 8 | "version": "1.1.4", 9 | "resolved": "https://registry.npmjs.org/@microsoft/recognizers-text/-/recognizers-text-1.1.4.tgz", 10 | "integrity": "sha512-hlSVXcaX5i8JcjuUJpVxmy2Z/GxvFXarF0KVySCFop57wNEnrLWMHe4I4DjP866G19VyIKRw+vPA32pkGhZgTg==" 11 | }, 12 | "@microsoft/recognizers-text-choice": { 13 | "version": "1.1.4", 14 | "resolved": "https://registry.npmjs.org/@microsoft/recognizers-text-choice/-/recognizers-text-choice-1.1.4.tgz", 15 | "integrity": "sha512-4CddwFe4RVhZeJgW65ocBrEdeukBMghK8pgI0K0Qy2eA5ysPZQpeZ7BGSDz5QMQei5LPY+QaAQ3CHU+ORHoO7A==", 16 | "requires": { 17 | "@microsoft/recognizers-text": "~1.1.4", 18 | "grapheme-splitter": "^1.0.2" 19 | } 20 | }, 21 | "@microsoft/recognizers-text-number": { 22 | "version": "1.1.4", 23 | "resolved": "https://registry.npmjs.org/@microsoft/recognizers-text-number/-/recognizers-text-number-1.1.4.tgz", 24 | "integrity": "sha512-6EmlR+HR+eJBIX7sQby1vs6LJB64wxLowHaGpIU9OCXFvZ5Nb0QT8qh10rC40v3Mtrz4DpScXfSXr9tWkIO5MQ==", 25 | "requires": { 26 | "@microsoft/recognizers-text": "~1.1.4", 27 | "bignumber.js": "^7.2.1", 28 | "lodash.escaperegexp": "^4.1.2", 29 | "lodash.sortby": "^4.7.0", 30 | "lodash.trimend": "^4.5.1" 31 | } 32 | }, 33 | "@microsoft/recognizers-text-sequence": { 34 | "version": "1.1.4", 35 | "resolved": "https://registry.npmjs.org/@microsoft/recognizers-text-sequence/-/recognizers-text-sequence-1.1.4.tgz", 36 | "integrity": "sha512-rb5j8/aE7HSOdIxaVfCGFrj0wWPpSq0CuykFg/A/iJNPP+FnAU71bgP5HexrwQcpCsDinauisX7u0DKIChrHRA==", 37 | "requires": { 38 | "@microsoft/recognizers-text": "~1.1.4", 39 | "grapheme-splitter": "^1.0.2" 40 | } 41 | }, 42 | "bignumber.js": { 43 | "version": "7.2.1", 44 | "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", 45 | "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" 46 | }, 47 | "chrono-node": { 48 | "version": "1.4.3", 49 | "resolved": "https://registry.npmjs.org/chrono-node/-/chrono-node-1.4.3.tgz", 50 | "integrity": "sha512-ZyKcnTcr8i7Mt9p4+ixMHEuR6+eMTrjYCL9Rm9TZHviLleCtcZoVzmr2uSc+Vg8MX1YbNCnPbEd4rfV8WvzLcw==", 51 | "requires": { 52 | "dayjs": "^1.8.19" 53 | } 54 | }, 55 | "dayjs": { 56 | "version": "1.8.20", 57 | "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.20.tgz", 58 | "integrity": "sha512-mH0MCDxw6UCGJYxVN78h8ugWycZAO8thkj3bW6vApL5tS0hQplIDdAQcmbvl7n35H0AKdCJQaArTrIQw2xt4Qg==" 59 | }, 60 | "grapheme-splitter": { 61 | "version": "1.0.4", 62 | "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", 63 | "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" 64 | }, 65 | "lodash.escaperegexp": { 66 | "version": "4.1.2", 67 | "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", 68 | "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" 69 | }, 70 | "lodash.sortby": { 71 | "version": "4.7.0", 72 | "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", 73 | "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" 74 | }, 75 | "lodash.trimend": { 76 | "version": "4.5.1", 77 | "resolved": "https://registry.npmjs.org/lodash.trimend/-/lodash.trimend-4.5.1.tgz", 78 | "integrity": "sha1-EoBENyhrmMrYmWt5QU4RMAEUCC8=" 79 | }, 80 | "validator": { 81 | "version": "10.11.0", 82 | "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", 83 | "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /packages/prompts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newbot-prompts", 3 | "newbot": { 4 | "minVersion": "0.3.0", 5 | "support": { 6 | "languages": [ 7 | "en_EN", 8 | "fr_FR" 9 | ], 10 | "platforms": "all" 11 | } 12 | }, 13 | "version": "1.0.1", 14 | "description": "Ask the user to enter text in a specific format", 15 | "main": "index.js", 16 | "keywords": [], 17 | "author": "NewBot", 18 | "license": "MIT", 19 | "dependencies": { 20 | "@microsoft/recognizers-text-choice": "^1.1.4", 21 | "@microsoft/recognizers-text-number": "^1.1.4", 22 | "@microsoft/recognizers-text-sequence": "^1.1.4", 23 | "chrono-node": "^1.4.3", 24 | "newbot-formats": "^1.7.1", 25 | "validator": "^10.11.0" 26 | }, 27 | "scripts": { 28 | "build": "newbot build --entry skills/prompts/prompts.js" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/prompts/readme.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Ask the user to enter text in a specific format. the chatbot can ask for : 4 | 5 | - An email 6 | - A phone number 7 | - A date 8 | - A confirmation 9 | - A number 10 | 11 | # Installation 12 | 13 | Install with 14 | 15 | `npm install newbot-prompts` 16 | 17 | # Use 18 | 19 | 1. In your skill, import the package 20 | 2. Add the skill in the `skills` 21 | 22 | ```js 23 | import prompts from 'newbot-prompts' 24 | import code from './main.converse' 25 | 26 | export default { 27 | code, 28 | skills: { 29 | prompts 30 | } 31 | } 32 | ``` 33 | 34 | # Use in ConverseScript 35 | 36 | In `main.converse` : 37 | 38 | ## Ask email 39 | 40 | ```ts 41 | myFunction() { 42 | email = prompts.email() 43 | > { email } 44 | } 45 | ``` 46 | 47 | ## Ask Phone 48 | 49 | ```ts 50 | myFunction() { 51 | phone = prompts.phone() 52 | > { phone } 53 | } 54 | ``` 55 | 56 | ## Ask Date 57 | 58 | ```ts 59 | myFunction() { 60 | date = prompts.time('Give a date') 61 | > { date } 62 | } 63 | ``` 64 | 65 | ## Ask Number 66 | 67 | ```ts 68 | myFunction() { 69 | number = prompts.number('Give a number') 70 | > { number } 71 | } 72 | ``` 73 | 74 | You can put a minimum and/or maximum : 75 | 76 | ```ts 77 | myFunction() { 78 | number = prompts.number('Give a number', { 79 | min: 0, 80 | max: 100 81 | }) 82 | > { number } 83 | } 84 | ``` 85 | 86 | ## Ask Confirmation 87 | ```ts 88 | myFunction() { 89 | bool = prompts.confirm('Do you want to buy?') 90 | > { bool } 91 | } 92 | ``` 93 | -------------------------------------------------------------------------------- /packages/storage/local/index.js: -------------------------------------------------------------------------------- 1 | import localforage from 'localforage' 2 | 3 | export default function ({ 4 | keyName = 'newbot-progress' 5 | } = {}) { 6 | return { 7 | finished(input, { 8 | user 9 | }) { 10 | const json = JSON.stringify(user.toJson()) 11 | localforage.setItem(keyName, json) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/storage/local/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newbot-localstorage", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "immediate": { 8 | "version": "3.0.6", 9 | "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", 10 | "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" 11 | }, 12 | "lie": { 13 | "version": "3.1.1", 14 | "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", 15 | "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", 16 | "requires": { 17 | "immediate": "~3.0.5" 18 | } 19 | }, 20 | "localforage": { 21 | "version": "1.7.3", 22 | "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.3.tgz", 23 | "integrity": "sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ==", 24 | "requires": { 25 | "lie": "3.1.1" 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/storage/local/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newbot-localstorage", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "localforage": "^1.7.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/train/extract.js: -------------------------------------------------------------------------------- 1 | 2 | const isArray = require('lodash/isArray') 3 | const uniq = require('lodash/uniq') 4 | const get = require('lodash/get') 5 | const clone = require('lodash/clone') 6 | 7 | class ExtractIntents { 8 | constructor(converse, manager) { 9 | this.manager = manager 10 | this.cacheLang = {} 11 | this.cache = [] 12 | this.languages = [] 13 | this.converse = converse 14 | } 15 | 16 | async getIntents() { 17 | const intents = await this.converse.getAllIntents() 18 | for (let intent of intents) { 19 | const langs = get(intent, '_skill.lang._list') 20 | if (langs) { 21 | this.languages = [ 22 | ...this.languages, 23 | ...langs.map(lang => lang.split('_')[0]) 24 | ] 25 | this.languages = uniq(this.languages) 26 | } 27 | let [intentName, utterances] = intent.params 28 | if (isArray(utterances)) { 29 | utterances = { 30 | en: utterances 31 | } 32 | } 33 | for (let lang in utterances) { 34 | if (lang[0] == '_') continue 35 | this.cacheLang[lang] = true 36 | for (let utterance of utterances[lang]) { 37 | this.cache.push({ 38 | params: [lang, utterance, intentName], 39 | converse: intent._skill 40 | }) 41 | } 42 | } 43 | } 44 | } 45 | 46 | translate() { 47 | const langFiles = this.languages 48 | let cacheClone = [] 49 | 50 | for (let i = 0 ; i < this.cache.length ; i++) { 51 | let { params } = this.cache[i] 52 | 53 | if (params[1][0] != '#') { 54 | cacheClone.push(clone(params)) 55 | continue 56 | } 57 | const translateAndMemorize = (instanceLang, langId, text) => { 58 | const translated = instanceLang.translate(text) 59 | cacheClone.push([langId, translated, this.cache[i].params[2]]) 60 | } 61 | 62 | for (let lang of langFiles) { 63 | let langId = lang 64 | const text = params[1].substr(1) 65 | const instanceLang = this.cache[i].converse.lang 66 | const fullLang = langId + '_' + langId.toUpperCase() 67 | instanceLang.set(fullLang) 68 | const group = instanceLang.getGroup(text) 69 | if (group.length > 0) { 70 | for (let gtext of group) { 71 | translateAndMemorize(instanceLang, langId, gtext) 72 | } 73 | } 74 | else { 75 | translateAndMemorize(instanceLang, langId, text) 76 | } 77 | } 78 | } 79 | for (let lang of langFiles) { 80 | this.cacheLang[lang] = true 81 | } 82 | this.cache = cacheClone 83 | } 84 | 85 | addDocuments() { 86 | for (let params of this.cache) { 87 | this.manager.addLanguage(params[0]) 88 | this.manager.addDocument(...params) 89 | } 90 | return this.manager 91 | } 92 | } 93 | 94 | module.exports = ExtractIntents -------------------------------------------------------------------------------- /packages/train/index.js: -------------------------------------------------------------------------------- 1 | const ExtractIntents = require('./extract') 2 | const { containerBootstrap } = require('@nlpjs/core') 3 | const { Nlp } = require('@nlpjs/nlp') 4 | const { BuiltinCompromise } = require('@nlpjs/builtin-compromise') 5 | 6 | module.exports = { 7 | async train(converse, langs = []) { 8 | const container = await containerBootstrap() 9 | container.use(Nlp) 10 | 11 | for (let lang of langs) { 12 | container.use(lang) 13 | } 14 | 15 | const builtin = new BuiltinCompromise() 16 | container.register('extract-builtin-??', builtin, true) 17 | 18 | const nlp = container.get('nlp') 19 | nlp.settings.autoSave = false 20 | 21 | const extract = new ExtractIntents(converse, nlp) 22 | await extract.getIntents() 23 | extract.translate() 24 | const manager = extract.addDocuments() 25 | await manager.train() 26 | return manager.export(true) 27 | } 28 | } -------------------------------------------------------------------------------- /packages/webpack/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') 3 | const nodeExternals = require('webpack-node-externals') 4 | const NodemonPlugin = require('nodemon-webpack-plugin') 5 | 6 | const mode = process.env.NODE_ENV || 'development' 7 | const prod = mode === 'production' 8 | 9 | module.exports = function(dirname, extend = {}) { 10 | 11 | const plugins = [] 12 | 13 | return { 14 | target: 'node', 15 | node: { 16 | __dirname: false 17 | }, 18 | externals: [nodeExternals()], 19 | mode, 20 | entry: `./src/server.js`, 21 | output: { 22 | path: path.join(dirname, 'dist/server'), 23 | filename: 'index.js' 24 | }, 25 | resolve: { 26 | extensions: ['.js'] 27 | }, 28 | module: { 29 | rules: [{ 30 | test: /\.converse$/i, 31 | use: [{ 32 | loader: path.resolve(__dirname, '../../loader', 'converse.js') 33 | }] 34 | }] 35 | }, 36 | optimization: { 37 | minimize: false 38 | }, 39 | plugins: [ 40 | new CleanWebpackPlugin(), 41 | new NodemonPlugin({ 42 | script: './dist/server/index.js', 43 | watch: path.resolve('./dist/server') 44 | }) 45 | ] 46 | } 47 | } -------------------------------------------------------------------------------- /packages/wit/.npmignore: -------------------------------------------------------------------------------- 1 | .build 2 | bot -------------------------------------------------------------------------------- /packages/wit/bot/main.converse: -------------------------------------------------------------------------------- 1 | @Intent('buy') 2 | buy() { 3 | > Ok, { :intent.contact.value } 4 | > Your date : { :intent.datetime.value } 5 | } -------------------------------------------------------------------------------- /packages/wit/bot/main.js: -------------------------------------------------------------------------------- 1 | import witSkill from '../' 2 | import code from './main.converse' 3 | 4 | export default { 5 | code, 6 | skills: { 7 | witSkill: witSkill({ 8 | accessToken: process.env.WIT_TOKEN 9 | }) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/wit/index.js: -------------------------------------------------------------------------------- 1 | const { 2 | Wit 3 | } = require('node-wit') 4 | 5 | module.exports = function ({ 6 | accessToken 7 | }) { 8 | 9 | const client = new Wit({ 10 | accessToken 11 | }) 12 | 13 | return { 14 | nlp: { 15 | async wit(text) { 16 | try { 17 | const responses = await client.message(text, {}) 18 | const intents = {} 19 | const entities = {} 20 | for (let intentKey in responses.entities) { 21 | if (intentKey == 'intent') continue 22 | const val = responses.entities[intentKey] 23 | entities[intentKey] = Object.assign({}, val[0]) 24 | entities[intentKey].values = val 25 | } 26 | for (let intent of responses.entities.intent) { 27 | intents[intent.value] = () => { 28 | return entities 29 | } 30 | } 31 | return intents 32 | } catch (err) { 33 | console.error(err) 34 | return { 35 | 'wit.error'() { 36 | return { 37 | error: err.message 38 | } 39 | } 40 | } 41 | } 42 | } 43 | }, 44 | shareNlp: true 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/wit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@newbot/wit", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "node-wit": "^5.0.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | NewBot 4 |

5 | 6 |
7 |

NewBot Javascript Framework

8 |
9 | 10 |
11 | 12 | **NewBot Framework is an open source Javascript framework for building chatbots 13 | 14 | Creating a conversational system can take a long time. The goal of NewBot Framework: to give all the means to realize chatbots / voicebots as quickly and as easily as possible 15 | 16 | ## Docs 17 | 18 | [Last version](https://docs.newbot.io) 19 | 20 | ## Why not use Microsoft BotFramework or BotKit? 21 | 22 | The approach is completely different from these frameworks. NewBot Framework is designed to create conversational systems without worrying about the final platform. 23 | 24 | We offer a framework, a skill structure and the means to test your bot. 25 | 26 | [5 good reasons to use NewBot Framework](https://medium.com/@NewBot/5-good-reasons-to-use-newbot-framework-5fee63839a8e) 27 | 28 | # Why NewBot Framework 29 | 30 | - **Modules**. A skill is an independent module. This helps structure the project and share skills 31 | - **NLP**. Native NLP, external services (DialogFlow, NewBot Cloud, etc.) or your own NLP system 32 | - **Easy PWA integration**. Create an offline chatbot with PWA 33 | - **Unit tests**. Create powerful unit tests 34 | - **Internationalization**. Easily integrate chatbot responses into multiple languages 35 | 36 | ## Installation 37 | 38 | ```bash 39 | npx degit newbotjs/template my-chatbot 40 | cd my-chatbot 41 | npm install 42 | npm run dev 43 | ``` 44 | 45 | ## Usage 46 | 47 | - `main.converse` 48 | 49 | ```ts 50 | @Event('start') 51 | start() { 52 | > Hello 53 | } 54 | ``` 55 | 56 | - `main.js` 57 | 58 | ```js 59 | import code from './main.converse' 60 | 61 | export default { 62 | code 63 | } 64 | ``` 65 | 66 | ## License 67 | 68 | MIT 69 | 70 | ## Changed 71 | 72 | ### 2021-09-08 73 | 74 | Set the version of NewBot. Works in WebWorkers 75 | 76 | ### 2020-02-19 77 | 78 | - Fix several bugs 79 | 80 | ### 2020-02-06 81 | 82 | - It is possible to train the chatbot on the fly 83 | - The training now uses version 4 of the NLPJS module. The generated model is therefore different 84 | - 1 additional browser-side file is generated (dist/newbot.with-nlp.[min].js). NLPJS content is integrated into the final file -------------------------------------------------------------------------------- /sample/index.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | -------------------------------------------------------------------------------- /sample/languages/fr_FR.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "hello": "salut" 4 | }, 5 | { 6 | "plurial": { 7 | "p": [ 8 | "s" 9 | ] 10 | } 11 | } 12 | ] -------------------------------------------------------------------------------- /sample/skills/hey/hey.converse: -------------------------------------------------------------------------------- 1 | @Intent(/hello/i) 2 | hey() { 3 | > Hey ! 4 | } -------------------------------------------------------------------------------- /sample/skills/hey/hey.js: -------------------------------------------------------------------------------- 1 | const { Converse } = require('conversescript') 2 | 3 | const converse = new Converse() 4 | converse.file(__dirname + '/hey.converse') 5 | 6 | converse.functions({ 7 | hello() { 8 | return 'Hey' 9 | } 10 | }) 11 | 12 | module.exports = converse -------------------------------------------------------------------------------- /sample/skills/map/map.component.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /sample/skills/map/map.converse: -------------------------------------------------------------------------------- 1 | @Intent('openMap', [ 2 | 'open map' 3 | ]) 4 | open() { 5 | > I open map 6 | } -------------------------------------------------------------------------------- /sample/skills/map/map.js: -------------------------------------------------------------------------------- 1 | import code from './map.converse' 2 | import component from './map.component.vue' 3 | 4 | export default { 5 | code, 6 | component 7 | } -------------------------------------------------------------------------------- /src/api/array.js: -------------------------------------------------------------------------------- 1 | const Array = { 2 | 3 | length(array) { 4 | return array.length 5 | }, 6 | 7 | push(array, value) { 8 | return array.push(value) 9 | }, 10 | 11 | pushOnlyUniq(array, value) { 12 | if (array.indexOf(value) === -1) { 13 | return this.push(array, value) 14 | } 15 | return array 16 | }, 17 | 18 | indexOf(array, value) { 19 | return array.indexOf(value) 20 | } 21 | } 22 | 23 | module.exports = Array -------------------------------------------------------------------------------- /src/api/debugger.js: -------------------------------------------------------------------------------- 1 | const _debugger = { 2 | 3 | $params: ['user', 'execution', 'level'], 4 | 5 | $call(user, execution, level) { 6 | const { namespace } = execution 7 | const render = (variables, title) => { 8 | console.log(`--- ${title} ---`) 9 | for (let name in variables) { 10 | console.log(`\n * ${name} = `, JSON.stringify(variables[name], null, 2)) 11 | } 12 | console.log('\n') 13 | } 14 | console.log('* Skill : ' + namespace) 15 | console.log('* User Id : ' + user.id) 16 | console.log('* Current Lang : ' + user.lang) 17 | render(user.variables[namespace], 'global variables') 18 | render(user.magicVar, 'magic variables') 19 | render(user.varFn[namespace][level], `${level}(...) function`) 20 | } 21 | } 22 | 23 | 24 | module.exports = _debugger -------------------------------------------------------------------------------- /src/api/eval.js: -------------------------------------------------------------------------------- 1 | const Interpreter = require('../interpreter') 2 | 3 | const Eval = { 4 | 5 | $params: ['users', 'execution'], 6 | 7 | json(json, users, execution) { 8 | try { 9 | const { converse, user, _input, options, propagate } = execution 10 | const obj = JSON.parse(json) 11 | const interpreter = new Interpreter(obj, users, execution.converse) 12 | return interpreter.exec(user, _input, options, propagate) 13 | } 14 | catch (err) { 15 | console.log(err) 16 | } 17 | } 18 | } 19 | 20 | 21 | module.exports = Eval -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | const Users = require('./users') 2 | const Array = require('./array') 3 | const Lang = require('./lang') 4 | const Utils = require('./utils') 5 | const Eval = require('./eval') 6 | const _debugger = require('./debugger') 7 | 8 | module.exports = { Users, Array, Lang, Utils, Eval, debugger: _debugger } -------------------------------------------------------------------------------- /src/api/lang.js: -------------------------------------------------------------------------------- 1 | const LANG_NAMES = { 2 | fr_FR: ['Français', 'French'], 3 | en_EN: ['English', 'English'] 4 | } 5 | 6 | const Lang = { 7 | 8 | $params: ['user'], 9 | 10 | set(lang, user) { 11 | if (lang) { 12 | user.setLang(lang) 13 | } 14 | }, 15 | 16 | name(user) { 17 | const lang = user.getLang() 18 | return LANG_NAMES[lang][0] 19 | } 20 | } 21 | 22 | 23 | module.exports = Lang -------------------------------------------------------------------------------- /src/api/users.js: -------------------------------------------------------------------------------- 1 | const Users = { 2 | 3 | $params: ['users'], 4 | 5 | nbUsers(users) { 6 | return users.size 7 | }, 8 | 9 | sum(name, users) { 10 | let sum = 0 11 | for (let [,user] of users) { 12 | sum += +user.getVariable(this.namespace, name) 13 | } 14 | return sum 15 | }, 16 | 17 | avg(name, users) { 18 | return this.sum(name, users) / this.nbUsers(users) 19 | } 20 | 21 | } 22 | 23 | 24 | module.exports = Users -------------------------------------------------------------------------------- /src/api/utils.js: -------------------------------------------------------------------------------- 1 | const Utils = { 2 | uniqid() { 3 | return new Date().valueOf().toString(36) + Math.random().toString(36).substr(2) 4 | } 5 | } 6 | 7 | module.exports = Utils -------------------------------------------------------------------------------- /src/debug/index.js: -------------------------------------------------------------------------------- 1 | class Debug { 2 | 3 | constructor(script, history) { 4 | this.script = script 5 | this.history = history 6 | } 7 | 8 | organize() { 9 | let code = this.script.split('\n') 10 | console.log(this.history[0]._file) 11 | } 12 | 13 | display() { 14 | const code = this.organize() 15 | console.log('--- %s ---', DEBUG) 16 | for (let line of code) { 17 | console.log(line) 18 | } 19 | console.log('------') 20 | } 21 | 22 | } 23 | 24 | module.exports = Debug -------------------------------------------------------------------------------- /src/decorators/Action.js: -------------------------------------------------------------------------------- 1 | const Decorator = require('./decorator') 2 | 3 | class DecoratorAction extends Decorator { 4 | 5 | } 6 | 7 | module.exports = DecoratorAction -------------------------------------------------------------------------------- /src/decorators/Condition.js: -------------------------------------------------------------------------------- 1 | const Decorator = require('./decorator') 2 | 3 | class DecoratorCondition extends Decorator { 4 | 5 | run(execution) { 6 | return new Promise(async (resolve, reject) => { 7 | const { converse, user, options } = execution 8 | for (let param of this.params) { 9 | let fn = converse._conditions[param] 10 | if (!fn) { 11 | reject(new Error(`${param} condition function not exists`)) 12 | return 13 | } 14 | let ret = fn(options.data, user) 15 | if (ret instanceof Promise) { 16 | ret = await ret 17 | } 18 | if (!ret) { 19 | resolve(false) 20 | return 21 | } 22 | } 23 | resolve(true) 24 | }) 25 | } 26 | 27 | } 28 | 29 | module.exports = DecoratorCondition -------------------------------------------------------------------------------- /src/decorators/Event.js: -------------------------------------------------------------------------------- 1 | const Decorator = require('./decorator') 2 | 3 | class DecoratorEvent extends Decorator { 4 | 5 | static get CAN_ACTIVATE() { 6 | return 'canActivate' 7 | } 8 | 9 | start() { 10 | return this.params[0] == 'start' 11 | } 12 | 13 | nothing() { 14 | return this.params[0] == 'nothing' 15 | } 16 | 17 | } 18 | 19 | module.exports = DecoratorEvent -------------------------------------------------------------------------------- /src/decorators/Intent.js: -------------------------------------------------------------------------------- 1 | const Decorator = require('./decorator') 2 | const _ = require('../utils/lodash') 3 | 4 | class IntentEvent extends Decorator { 5 | 6 | run(execution) { 7 | return new Promise((resolve, reject) => { 8 | const { intents } = execution 9 | if (this.instructions && this.instructions.length > 0) { 10 | execution.execBlock({ 11 | instructions: this.instructions 12 | }, 0, 'test', () => { 13 | resolve(true) 14 | }) 15 | return 16 | } 17 | if (!intents) { 18 | return resolve(false) 19 | } 20 | 21 | const intentName = this.params[0] 22 | let intent = intents[intentName] 23 | if (intentName.regexp) { 24 | const regexp = new RegExp(intentName.regexp, intentName.flags.join('')) 25 | if (regexp.test(execution.input)) { 26 | return resolve(true) 27 | } 28 | } 29 | if (_.isFunction(intent)) { 30 | intent = intent() 31 | } 32 | if (intent) { 33 | execution.setMagicVar(`intent`, intent) 34 | return resolve(true) 35 | } 36 | resolve(false) 37 | }) 38 | } 39 | 40 | } 41 | 42 | module.exports = IntentEvent -------------------------------------------------------------------------------- /src/decorators/decorator.js: -------------------------------------------------------------------------------- 1 | const _ = require('../utils/lodash') 2 | 3 | class Decorator { 4 | 5 | constructor(decorator, fnName) { 6 | this.decorator = decorator 7 | this.otherDecorators = {} 8 | this.name = this.decorator.name 9 | this.instructions = decorator.instructions 10 | this.params = decorator.params 11 | this.fnName = fnName 12 | } 13 | 14 | findParam(params) { 15 | const diff = _.difference(this.params, params) 16 | return diff.length === 0 17 | } 18 | 19 | } 20 | 21 | module.exports = Decorator -------------------------------------------------------------------------------- /src/decorators/index.js: -------------------------------------------------------------------------------- 1 | const DecoratorEvent = require('./Event') 2 | const Intent = require('./Intent') 3 | const Action = require('./Action') 4 | const Condition = require('./Condition') 5 | 6 | module.exports = { Event: DecoratorEvent, Intent, Action, Condition } -------------------------------------------------------------------------------- /src/error/index.js: -------------------------------------------------------------------------------- 1 | const ID = { 2 | 'variable.not.defined': ['ReferenceError', ins => `${ins.variable} is not defined`], 3 | 'function.not.defined': ['ReferenceError', ins => `${ins.name}(...) function is not defined`], 4 | 'arithmetic.error': ['ArithmeticError', (ins, err) => 'Expression is not correct. ' + err.message], 5 | 'type.error': ['TypeError', (ins, err) => 'Type is not correct. ' + err.message] 6 | } 7 | 8 | class ExecutionError { 9 | 10 | constructor(script, namespace, reject) { 11 | this.script = script 12 | this.namespace = namespace 13 | this.reject = reject 14 | } 15 | 16 | throw(ins, id, err) { 17 | let error, code, msg 18 | if (!ins) { 19 | throw err 20 | } 21 | if (ID[id]) { 22 | code = ID[id][0] 23 | msg = ID[id][1] 24 | } 25 | else { 26 | code = 'InternalError' 27 | msg = () => err.message 28 | } 29 | msg = msg(ins, err) 30 | if (ins._file) { 31 | let { line, column } = ins._file.start 32 | error = this.makeError(code, msg, { 33 | line, 34 | column 35 | }) 36 | } 37 | else { 38 | error = new Error(msg); 39 | } 40 | if (this.on) this.on(error) 41 | if (this.reject) { 42 | return error 43 | } 44 | else { 45 | throw error 46 | } 47 | } 48 | 49 | syntax(err) { 50 | if (!err.location) { 51 | throw err 52 | } 53 | const { line, column } = err.location.start 54 | const error = this.makeError('syntax', 'Syntax Error:' + err.message, { 55 | line, 56 | column 57 | }) 58 | if (this.reject) { 59 | this.reject(error) 60 | } 61 | else { 62 | throw error 63 | } 64 | } 65 | 66 | makeError(code, message, options) { 67 | var line = options.line; 68 | var column = options.column; 69 | var filename = options.filename; 70 | var src = this.script || options.src; 71 | var fullMessage; 72 | var location = line + (column ? ':' + column : ''); 73 | 74 | const skillPath = this.namespace ? this.namespace 75 | .split('-') 76 | .join(' / ') : 'unknow' 77 | const strSkill = `Skill : ${skillPath}. Line ${line}` 78 | 79 | if (src && typeof src == 'string' && line >= 1 && line <= src.split('\n').length) { 80 | var lines = src.split('\n'); 81 | var start = Math.max(line - 3, 0); 82 | var end = Math.min(lines.length, line + 3); 83 | // Error context 84 | var context = lines.slice(start, end).map(function (text, i) { 85 | var curr = i + start + 1; 86 | var preamble = (curr == line ? ' > ' : ' ') 87 | + curr 88 | + '| '; 89 | var out = preamble + text; 90 | if (curr === line && column > 0) { 91 | out += '\n'; 92 | out += Array(preamble.length + column).join('-') + '^'; 93 | } 94 | return out; 95 | }).join('\n'); 96 | fullMessage = strSkill + '\n\n' + context + '\n\n' + message 97 | } else { 98 | fullMessage = strSkill + ':' + location + '\n\n' + message; 99 | } 100 | 101 | const err = new Error(fullMessage); 102 | err.skill = skillPath 103 | err.line = line 104 | err.column = column 105 | return err; 106 | } 107 | } 108 | 109 | module.exports = ExecutionError -------------------------------------------------------------------------------- /src/nlp/index.js: -------------------------------------------------------------------------------- 1 | const _ = require('../utils/lodash') 2 | 3 | class Nlp { 4 | 5 | constructor(name, converse, options = {}) { 6 | this.name = name 7 | this.converse = converse 8 | this.options = options 9 | this.uuid = options.uuid 10 | this.priority = this.options.priority ? this.options.priority : undefined 11 | this.intents = {} 12 | } 13 | 14 | add(intents) { 15 | if (_.isFunction(intents)) { 16 | this.intents = intents 17 | return 18 | } 19 | this.intents = _.merge(this.intents, intents) 20 | } 21 | 22 | exec(input, userId, converse = this.converse) { 23 | return new Promise((resolve, reject) => { 24 | if (converse._mockNlp && converse._mockNlp[this.name]) { 25 | const ret = this.converse._mockNlp[this.name](input, userId) 26 | resolve({ structured: ret }) 27 | return 28 | } 29 | if (_.isFunction(this.intents)) { 30 | this.intents(input, userId, converse).then((intents) => { 31 | resolve({ intents }) 32 | }) 33 | return 34 | } 35 | resolve() 36 | }) 37 | .then(({ structured, intents } = {}) => { 38 | intents = intents || this.intents 39 | let filterIntents = {} 40 | for (let key in intents) { 41 | let intent = intents[key] 42 | let user = typeof userId == 'string' ? converse._users.get(userId) : userId 43 | let ret = intent(input, structured, user) 44 | if (ret) { 45 | filterIntents[key] = ret 46 | } 47 | } 48 | return filterIntents 49 | }) 50 | } 51 | 52 | } 53 | 54 | module.exports = Nlp -------------------------------------------------------------------------------- /src/nlp/native.js: -------------------------------------------------------------------------------- 1 | module.exports = function processNlp(manager) { 2 | return async (text, userId, converse) => { 3 | 4 | const result = await manager.process(text) 5 | const taln = {} 6 | for (let entity of result.entities) { 7 | let value 8 | if (entity.resolution) { 9 | value = entity.resolution 10 | } 11 | else if (entity.utteranceText) { 12 | value = { value: entity.utteranceText } 13 | } 14 | taln[entity.entity] = value 15 | } 16 | taln.sentiment = result.sentiment.vote 17 | return { 18 | [result.intent](text, _, user) { 19 | user.setMagicVariable('entity', taln) 20 | return result 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/testing/assert.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | 3 | class Assert { 4 | constructor(testing) { 5 | this.testing = testing 6 | } 7 | _getUser() { 8 | return this.testing.converse.users.get(this.testing.id) 9 | } 10 | output(index) { 11 | if (_.isUndefined(index)) { 12 | return this.testing._output 13 | } 14 | return this.testing._output[index] 15 | } 16 | variable(name) { 17 | const user = this._getUser() 18 | return user.getVariableInFonction('default', this.testing.currentLevel, name) 19 | } 20 | userVariable(name) { 21 | const user = this._getUser() 22 | return user.getVariable('default', name) 23 | } 24 | magicVariable(name) { 25 | const user = this._getUser() 26 | return user.getMagicVariable(name) 27 | } 28 | } 29 | 30 | module.exports = Assert -------------------------------------------------------------------------------- /src/testing/conversation/bot.js: -------------------------------------------------------------------------------- 1 | module.exports = function(str) { 2 | return { 3 | type: 'bot', 4 | str: str.raw ? str.raw[0] : str 5 | } 6 | } -------------------------------------------------------------------------------- /src/testing/conversation/user.js: -------------------------------------------------------------------------------- 1 | module.exports = function(str) { 2 | return { 3 | type: 'user', 4 | str: str.raw ? str.raw[0] : str 5 | } 6 | } -------------------------------------------------------------------------------- /src/testing/converse-testing.js: -------------------------------------------------------------------------------- 1 | const Converse = require('../converse') 2 | const UserTesting = require('./user-testing') 3 | 4 | class ConverseTesting extends Converse { 5 | 6 | constructor(...args) { 7 | super(...args) 8 | this.testing = true 9 | this._mock = {} 10 | this._mockNlp = {} 11 | } 12 | 13 | createUser(data) { 14 | return new UserTesting(this, data) 15 | } 16 | 17 | mock(fnName, callback) { 18 | this._mock[fnName] = callback 19 | } 20 | 21 | mockNlp(name, callback) { 22 | this._mockNlp[name] = callback 23 | } 24 | 25 | testingWrapper(callback) { 26 | this._testingWrapper = callback 27 | } 28 | 29 | } 30 | 31 | module.exports = ConverseTesting -------------------------------------------------------------------------------- /src/transpiler/lexer.js: -------------------------------------------------------------------------------- 1 | const _ = require('../utils/lodash') 2 | const browser = require('../utils/browser') 3 | const ExecutionError = require('../error') 4 | 5 | if (typeof window != 'undefined') window._ = _ 6 | 7 | class Transpiler { 8 | 9 | constructor(script, namespace) { 10 | this.variables = {} 11 | this._script = script 12 | this.namespace = namespace 13 | this.parser = Transpiler.newbotParser 14 | } 15 | 16 | run() { 17 | if (browser.is() && !this.parser) { 18 | throw 'You can not use the parser. Integrate rather the "newbot.with-parser.min.js" file' 19 | } 20 | try { 21 | return this.parser.parse(this._script) 22 | } 23 | catch (err) { 24 | const error = new ExecutionError(this._script, this.namespace) 25 | error.syntax(err) 26 | } 27 | } 28 | 29 | line(str) { 30 | str = str.trim() 31 | return this.parser.parse(str) 32 | } 33 | 34 | isVariable() { 35 | // 36 | } 37 | } 38 | 39 | module.exports = Transpiler -------------------------------------------------------------------------------- /src/transpiler/load.js: -------------------------------------------------------------------------------- 1 | const peg = require('pegjs') 2 | const grammar = require('./grammar') 3 | const Transpiler = require('./lexer') 4 | const parser = peg.generate(grammar) 5 | 6 | Transpiler.newbotParser = parser 7 | -------------------------------------------------------------------------------- /src/user.js: -------------------------------------------------------------------------------- 1 | const _ = require('./utils/lodash') 2 | 3 | class User { 4 | 5 | constructor(id) { 6 | this.address = {} 7 | this._infoAddress = { 8 | lock: {}, 9 | actived: {} 10 | } 11 | this._realSkill = {} 12 | this.varFn = {} 13 | this.magicVar = {} 14 | this.variables = {} 15 | this.id = id 16 | this.lang = null 17 | this._history = [] 18 | this._nlpCache = {} 19 | } 20 | 21 | _createNamespace(namespace) { 22 | if (!this.address[namespace]) { 23 | this.address[namespace] = [] 24 | } 25 | if (!this.variables[namespace]) { 26 | this.variables[namespace] = {} 27 | } 28 | if (!this.varFn[namespace]) { 29 | this.varFn[namespace] = {} 30 | } 31 | } 32 | 33 | addAddress(address, namespace) { 34 | this._createNamespace(namespace) 35 | this.address[namespace].push(address) 36 | } 37 | 38 | getAddress(namespace) { 39 | this._createNamespace(namespace) 40 | return _.last(this.address[namespace]) 41 | } 42 | 43 | hasAddress(namespace) { 44 | this._createNamespace(namespace) 45 | return this.address[namespace].length > 0 46 | } 47 | 48 | clearAddress(namespace) { 49 | this._createNamespace(namespace) 50 | this.address[namespace] = [] 51 | } 52 | 53 | popAddress(namespace) { 54 | this._createNamespace(namespace) 55 | this.address[namespace].pop() 56 | } 57 | 58 | garbage(level, namespace) { 59 | this._createNamespace(namespace) 60 | delete this.varFn[namespace][level] 61 | } 62 | 63 | hasLockAddressStack(namespace) { 64 | return !!this._infoAddress.lock[namespace] 65 | } 66 | 67 | lockAddressStack(namespace, fnName, { 68 | activated 69 | } = {}) { 70 | this._infoAddress.lock[namespace] = fnName 71 | if (activated) { 72 | this._infoAddress.actived[namespace] = fnName 73 | } 74 | } 75 | 76 | unlockAddressStack(namespace, fnName, { 77 | activated 78 | } = {}) { 79 | if (this._infoAddress.lock[namespace] === fnName) { 80 | delete this._infoAddress.lock[namespace] 81 | if (activated) { 82 | delete this._infoAddress.actived[namespace] 83 | } 84 | } 85 | } 86 | 87 | addressStackIslocked(namespace) { 88 | return this._infoAddress.lock[namespace] 89 | } 90 | 91 | getVariables(namespace) { 92 | this._createNamespace(namespace) 93 | return this.variables[namespace] 94 | } 95 | 96 | getVariable(namespace, name) { 97 | name = name.replace(/^\$/, '') 98 | this._createNamespace(namespace) 99 | return this.variables[namespace][name] 100 | } 101 | 102 | setVariable(namespace, name, value) { 103 | name = name.replace(/^\$/, '') 104 | this._createNamespace(namespace) 105 | this.variables[namespace][name] = value 106 | } 107 | 108 | getVariableInFonction(namespace, fnName, varName) { 109 | this._createNamespace(namespace) 110 | if (!this.varFn[namespace][fnName]) { 111 | this.varFn[namespace][fnName] = {} 112 | } 113 | if (!varName) { 114 | return this.varFn[namespace][fnName] 115 | } 116 | return this.varFn[namespace][fnName][varName] 117 | } 118 | 119 | setVariableInFonction(namespace, fnName, varName, value) { 120 | this._createNamespace(namespace) 121 | if (!this.varFn[namespace][fnName]) { 122 | this.varFn[namespace][fnName] = {} 123 | } 124 | return this.varFn[namespace][fnName][varName] = value 125 | } 126 | 127 | getMagicVariable(name) { 128 | return this.magicVar[name] 129 | } 130 | 131 | setMagicVariable(name, value) { 132 | this.magicVar[name] = value 133 | } 134 | 135 | saveSession(session) { 136 | this.session = session 137 | } 138 | 139 | retrieveSession() { 140 | return this.session 141 | } 142 | 143 | setRealSkill(skillName, index) { 144 | this._realSkill[skillName] = index 145 | } 146 | 147 | getRealSkill(skillName) { 148 | return this._realSkill[skillName] || 0 149 | } 150 | 151 | setLang(lang) { 152 | this.lang = lang 153 | } 154 | 155 | getLang() { 156 | return this.lang 157 | } 158 | 159 | addHistory(ins) { 160 | this._history.push(ins) 161 | } 162 | 163 | resetHistory(ins) { 164 | this._history = [] 165 | } 166 | 167 | toJson() { 168 | return { 169 | _current: { 170 | _address: this.address, 171 | _var: this.varFn, 172 | _magicVar: this.magicVar, 173 | _infoAddress: this._infoAddress 174 | }, 175 | _session: this.session, 176 | data: this.variables, 177 | lang: this.lang, 178 | id: this.id 179 | } 180 | } 181 | 182 | fromJson(json) { 183 | this.address = json._current._address || {} 184 | this._infoAddress = json._current._infoAddress || { lock: {} } 185 | this.varFn = json._current._var || {} 186 | this.magicVar = json._current._magicVar || {} 187 | this.session = json._session || {} 188 | this.variables = json.data || {} 189 | this.lang = json.lang 190 | this.id = json.id 191 | return this 192 | } 193 | 194 | } 195 | 196 | module.exports = User -------------------------------------------------------------------------------- /src/utils/async.js: -------------------------------------------------------------------------------- 1 | class Async { 2 | async map(array, cb) { 3 | for (let i = 0 ; i < array.length ; i++) { 4 | array[i] = await cb(array[i]) 5 | } 6 | return array 7 | } 8 | } 9 | 10 | module.exports = new Async() -------------------------------------------------------------------------------- /src/utils/browser.js: -------------------------------------------------------------------------------- 1 | class Browser { 2 | is() { 3 | return typeof navigator != 'undefined' 4 | } 5 | } 6 | 7 | module.exports = new Browser() -------------------------------------------------------------------------------- /src/utils/fs.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const browser = require('./browser') 3 | 4 | class FileSystem { 5 | readFile(file, encode) { 6 | if (browser.is()) { 7 | return new Promise((resolve, reject) => { 8 | const xmlhttp = new XMLHttpRequest(); 9 | xmlhttp.onreadystatechange = function() { 10 | if (xmlhttp.readyState == XMLHttpRequest.DONE) { 11 | if (xmlhttp.status == 200) { 12 | resolve(xmlhttp.responseText) 13 | } 14 | else { 15 | reject(xmlhttp) 16 | } 17 | } 18 | } 19 | xmlhttp.open("GET", file, true) 20 | xmlhttp.send() 21 | }) 22 | } 23 | return new Promise((resolve, reject) => { 24 | fs.readFile(file, encode, (err, data) => { 25 | if (err) return reject(err) 26 | resolve(data) 27 | }) 28 | }) 29 | } 30 | } 31 | 32 | module.exports = new FileSystem() -------------------------------------------------------------------------------- /src/utils/is-promise.js: -------------------------------------------------------------------------------- 1 | module.exports = function isPromise(obj) { 2 | return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'; 3 | } -------------------------------------------------------------------------------- /src/utils/lodash.js: -------------------------------------------------------------------------------- 1 | const merge = require('lodash/merge') 2 | const isString = require('lodash/isString') 3 | const isObjectLike = require('lodash/isObjectLike') 4 | const isFunction = require('lodash/isFunction') 5 | const clone = require('lodash/clone') 6 | const cloneDeep = require('lodash/cloneDeep') 7 | const isArray = require('lodash/isArray') 8 | const set = require('lodash/set') 9 | const get = require('lodash/get') 10 | const isUndefined = require('lodash/isUndefined') 11 | const isNull = require('lodash/isNull') 12 | const isBoolean = require('lodash/isBoolean') 13 | const random = require('lodash/random') 14 | const last = require('lodash/last') 15 | const isNaN = require('lodash/isNaN') 16 | const flatten = require('lodash/flatten') 17 | const difference = require('lodash/difference') 18 | const isPlainObject = require('lodash/isPlainObject') 19 | const isNumber = require('lodash/isNumber') 20 | 21 | module.exports = { 22 | merge, 23 | isString, 24 | isObjectLike, 25 | isPlainObject, 26 | isFunction, 27 | clone, 28 | cloneDeep, 29 | isArray, 30 | get, 31 | set, 32 | isUndefined, 33 | isNull, 34 | isBoolean, 35 | isNumber, 36 | random, 37 | last, 38 | flatten, 39 | isNaN, 40 | difference 41 | } -------------------------------------------------------------------------------- /testing.js: -------------------------------------------------------------------------------- 1 | const { ConverseTesting } = require('./index') 2 | const bot = require('./src/testing/conversation/bot') 3 | const user = require('./src/testing/conversation/user') 4 | 5 | process.on('unhandledRejection', (reason, p) => { 6 | console.error('Unhandled Rejection at:', p, 'reason:', reason) 7 | }) 8 | 9 | module.exports = { ConverseTesting, bot, user } -------------------------------------------------------------------------------- /tests/interpreter.spec.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const { assert } = require('chai') 3 | const SystemJs = require('systemjs') 4 | const { ConverseTesting } = require('../testing') 5 | 6 | ConverseTesting.loader({ 7 | systemjs: SystemJs 8 | }) 9 | 10 | describe('Test Converse Testing', () => { 11 | 12 | let tests = [] 13 | let dir = `${__dirname}/interpreter` 14 | 15 | let files = fs.readdirSync(dir) 16 | 17 | for (let file of files) { 18 | let match = /(^[^\.]+)/.exec(file) 19 | let [, pattern] = match 20 | if (tests.indexOf(pattern) >= 0) { 21 | continue 22 | } 23 | tests.push(pattern) 24 | it(`${file}`, () => { 25 | let converse = new ConverseTesting() 26 | converse.file(`${dir}/${pattern}.converse`) 27 | let user = converse.createUser() 28 | let p = require(`./interpreter/${pattern}.spec`)(user, assert, converse) 29 | return p 30 | }) 31 | } 32 | 33 | }) 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /tests/interpreter/action.converse: -------------------------------------------------------------------------------- 1 | @Action('buy') 2 | buy() { 3 | > transaction : {:action} 4 | } -------------------------------------------------------------------------------- /tests/interpreter/action.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function (user, assert, converse) { 2 | return user 3 | .action('buy', '1337', function () { 4 | assert.deepEqual(this.output(), [ 5 | 'transaction : 1337' 6 | ]) 7 | }) 8 | .end() 9 | } -------------------------------------------------------------------------------- /tests/interpreter/array.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | array = [1, 2] 4 | > { Array.length(array) } 5 | length = Array.length(array) > 1 6 | (length) > ok 7 | } -------------------------------------------------------------------------------- /tests/interpreter/array.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function (user, assert, converse) { 2 | return user 3 | .start() 4 | .spy('start', function (testing) { 5 | assert.equal(testing.output(0), ['2']) 6 | assert.equal(testing.output(1), ['ok']) 7 | }) 8 | .end() 9 | } -------------------------------------------------------------------------------- /tests/interpreter/condition.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | 4 | val = 0 5 | 6 | bool1 = true 7 | opposite1 = !bool1 8 | 9 | bool2 = bool1 && opposite1 10 | bool3 = bool1 || opposite1 11 | bool4 = true && bool3 && bool2 12 | (bool1) { 13 | val++ 14 | Input() 15 | > a 16 | (true) { 17 | > b 18 | Input() 19 | > {:text} 20 | (true) { 21 | val++ 22 | } 23 | } 24 | > c 25 | } 26 | > d 27 | Input() 28 | (:text == 'ok') > e 29 | } 30 | -------------------------------------------------------------------------------- /tests/interpreter/condition.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function(user, assert, converse) { 2 | return user 3 | .start() 4 | .spy('start', function() { 5 | assert.equal(this.variable('opposite1'), false) 6 | assert.equal(this.variable('bool2'), false) 7 | assert.equal(this.variable('bool3'), true) 8 | assert.equal(this.variable('bool4'), false) 9 | assert.equal(this.variable('val'), 2, 'first value') 10 | }) 11 | .input('noop1', function() { 12 | assert.deepEqual(this.output(), [ 'a', 'b' ]) 13 | assert.equal(this.variable('val'), 1, 'first value') 14 | }) 15 | .input('noop2', function() { 16 | assert.deepEqual(this.output(), [ 'noop2', 'c', 'd' ]) 17 | assert.equal(this.variable('val'), 2, 'second value') 18 | }) 19 | .input('ok', function() { 20 | assert.deepEqual(this.output(), [ 'e' ]) 21 | }) 22 | .end() 23 | } 24 | -------------------------------------------------------------------------------- /tests/interpreter/expression.converse: -------------------------------------------------------------------------------- 1 | foo = 1 2 | test = foo + 10 - 9 // 2 3 | foo++ // 2 4 | test += foo // 4 5 | $score = test / 2 // 2 6 | 7 | @Event('start') 8 | start() { 9 | foo = 1 10 | $score += foo // 3 11 | } -------------------------------------------------------------------------------- /tests/interpreter/expression.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function(user, assert) { 2 | return user 3 | .start() 4 | .spy('start', function() { 5 | assert.equal(this.variable('foo'), 1) 6 | assert.equal(this.userVariable('score'), 3) 7 | }) 8 | .end() 9 | } -------------------------------------------------------------------------------- /tests/interpreter/format.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | val = 42 4 | 5 | @Format('button', 42) 6 | > ok 7 | 8 | } -------------------------------------------------------------------------------- /tests/interpreter/format.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function (user, assert, converse) { 2 | 3 | converse.format('button', function (text, [number]) { 4 | return { 5 | type: 'button', 6 | number, 7 | text 8 | } 9 | }) 10 | 11 | return user 12 | .start() 13 | .spy('start', function () { 14 | const [output] = this.output() 15 | assert.deepEqual(output, { 16 | type: 'button', 17 | number: 42, 18 | text: 'ok' 19 | }) 20 | }) 21 | .end() 22 | } -------------------------------------------------------------------------------- /tests/interpreter/function.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | > test 4 | foo('hello') 5 | Prompt() 6 | a = 1 7 | b = 2 8 | // val = math(a, b) 9 | > 3 10 | > end 11 | } 12 | 13 | 14 | foo(bar) { 15 | > {bar} 16 | } 17 | 18 | math(one, two) { 19 | return one + two 20 | } -------------------------------------------------------------------------------- /tests/interpreter/function.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function(user, assert) { 2 | return user 3 | .start(testing => { 4 | assert.equal(testing.output(0), 'test') 5 | assert.equal(testing.output(1), 'hello') 6 | }) 7 | .prompt('test', testing => { 8 | assert.equal(testing.output(0), '3') 9 | }) 10 | .end() 11 | } -------------------------------------------------------------------------------- /tests/interpreter/functions.converse: -------------------------------------------------------------------------------- 1 | language() { 2 | myvar = 1 3 | > what your language ? 4 | Input() 5 | > thanks 6 | } 7 | 8 | @Event('start') 9 | start() { 10 | myvar = 2 11 | language() 12 | > your name ? 13 | Input() 14 | } -------------------------------------------------------------------------------- /tests/interpreter/functions.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function(user, assert) { 2 | return user 3 | .start(function() { 4 | const [output] = this.output() 5 | assert.equal(output, 'what your language ?') 6 | assert.equal(this.variable('myvar'), 1) 7 | }) 8 | .input('fr', function() { 9 | assert.deepEqual(this.output(), ['thanks', 'your name ?']) 10 | assert.equal(this.variable('myvar'), 2) 11 | }) 12 | .end() 13 | } -------------------------------------------------------------------------------- /tests/interpreter/group.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | --- 4 | > hello 5 | > hey 6 | --- 7 | } -------------------------------------------------------------------------------- /tests/interpreter/group.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function (user, assert) { 2 | return user 3 | .start(testing => { 4 | assert.ok(['hello', 'hey'].indexOf(testing.output(0)) !== -1) 5 | }) 6 | .end() 7 | } -------------------------------------------------------------------------------- /tests/interpreter/input.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | > your name 4 | Input() 5 | > hello {:text} 6 | } -------------------------------------------------------------------------------- /tests/interpreter/input.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function (user, assert) { 2 | return user 3 | .start(function () { 4 | const [output] = this.output() 5 | assert.equal(output, 'your name') 6 | }) 7 | .input('sam', function () { 8 | const [output] = this.output() 9 | assert.equal(output, 'hello sam') 10 | assert.equal(this.magicVariable('text'), 'sam') 11 | }) 12 | .end() 13 | } -------------------------------------------------------------------------------- /tests/interpreter/intent.converse: -------------------------------------------------------------------------------- 1 | @Intent('departure') { 2 | (unknown :intent.city) > What is your city? 3 | } 4 | search() { 5 | > Your city : {:intent.city} 6 | } -------------------------------------------------------------------------------- /tests/interpreter/intent.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function (user, assert, converse) { 2 | 3 | converse.nlp('regexp', { 4 | departure(str) { 5 | const match = /[a-z ]+([A-Z][a-z]+)?/.exec(str) 6 | if (!match) return false 7 | return { city: match[1] } 8 | } 9 | }) 10 | 11 | return user 12 | .prompt('i want to go', testing => { 13 | assert.equal(testing.output(0), 'What is your city?') 14 | }) 15 | .end() 16 | } -------------------------------------------------------------------------------- /tests/interpreter/lang.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | @Format('quick', ['Français', 'Anglais', 'Espagnol']) 4 | > Choise your language 5 | Prompt() 6 | Lang.set('fr_FR') 7 | > hello 8 | > Your language : { Lang.name() } 9 | } -------------------------------------------------------------------------------- /tests/interpreter/lang.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function (user, assert, converse) { 2 | 3 | converse.configure({ 4 | languages: { 5 | path: __dirname + '/../languages', 6 | packages: ['en_EN', 'fr_FR'] 7 | } 8 | }).loadLanguage() 9 | 10 | converse.format('quick', function (text, [responses]) { 11 | return { 12 | type: 'quick', 13 | responses, 14 | text 15 | } 16 | }) 17 | 18 | return user 19 | .start() 20 | .prompt('Français', testing => { 21 | assert.equal(testing.output(0), 'Salut') 22 | assert.equal(testing.output(1), 'Your language : Français') 23 | }) 24 | .end() 25 | } -------------------------------------------------------------------------------- /tests/interpreter/network.converse: -------------------------------------------------------------------------------- 1 | $score = 0 2 | 3 | @Event('start') 4 | start() { 5 | > share your score 6 | Input() 7 | $score = :text 8 | > your average score : 9 | avg = Users.avg('score') 10 | > {avg} 11 | } -------------------------------------------------------------------------------- /tests/interpreter/network.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function(user, assert, converse) { 2 | 3 | function createUser(input, avg) { 4 | const newUser = converse.createUser() 5 | return newUser 6 | .spy('start', function() { 7 | const [,output] = this.output() 8 | assert.equal(this.variable('avg'), avg) 9 | assert.equal(output, avg) 10 | }) 11 | .start() 12 | .input(input) 13 | .end() 14 | } 15 | 16 | return createUser(1, 1) // 1 user, 1 input ; avg = 1 / 1 17 | .then(() => createUser(3, 2)) // 2 users, 3 input ; avg = (1+3) / 2 18 | .then(() => createUser(1, 5/3)) // 3 users, 1 input ; avg = (1+3+1) / 3 = 5/3 19 | .then(() => createUser(10, 15/4)) // 4 users, 10 input ; avg = (1+3+1+10) / 4 = 15/4 20 | 21 | } -------------------------------------------------------------------------------- /tests/interpreter/nlp.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | 4 | } 5 | 6 | @Intent('input.departure') 7 | search() { 8 | > search 9 | } -------------------------------------------------------------------------------- /tests/interpreter/nlp.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function (user, assert, converse) { 2 | 3 | converse.nlp('regexp', { 4 | 'input.departure'(str) { 5 | return /cherche/.test(str) ? { search: true } : null 6 | } 7 | }, { priority: 1 }) 8 | 9 | converse.nlp('other-regexp', { 10 | hey(str) { 11 | return /hey/.test(str) 12 | } 13 | }, { priority: 0 }) 14 | 15 | return user 16 | .start() 17 | .input('je cherche une maison') 18 | .spy('search', function () { 19 | const [output] = this.output() 20 | assert.equal(output, 'search') 21 | }) 22 | .end() 23 | } -------------------------------------------------------------------------------- /tests/interpreter/object.converse: -------------------------------------------------------------------------------- 1 | $global = {} 2 | 3 | @Event('start') 4 | start() { 5 | twoStr = 'two' 6 | threeStr = { 7 | str: ['three', 'last'] 8 | } 9 | value = { 10 | a: 'A', 11 | b: 'B', 12 | one: 1, 13 | more: { 14 | last: 1337 15 | } 16 | } 17 | other = { 18 | two: 2 19 | object: { 20 | deep: { 21 | three: 3 22 | } 23 | } 24 | } 25 | empty = {} 26 | 27 | calc = value.one + other.two + other.object.deep.three 28 | other.four = 4 29 | 30 | array = [1, 2, 3] 31 | array[1] = 4 32 | arraySum = array[0] + array[2] 33 | 34 | two = other[twoStr] 35 | four = array[value.one] 36 | 37 | deep = other['object'].deep[threeStr['str'][0]] 38 | value.more[threeStr.str[1]]++ 39 | value['c'] = 1 40 | 41 | empty[12] = 2 42 | 43 | $global['test'] = 'test' 44 | 45 | other2 = { 46 | data: { 47 | metadata: { 48 | text: other.four 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /tests/interpreter/object.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function (user, assert) { 2 | return user 3 | .start() 4 | .spy('start', function () { 5 | assert.equal(this.variable('calc'), 6) 6 | 7 | const other = this.variable('other') 8 | assert.equal(other.four, 4) 9 | assert.deepEqual(this.variable('array'), [1, 4, 3]) 10 | assert.equal(this.variable('arraySum'), 4) 11 | assert.equal(this.variable('two'), 2) 12 | assert.equal(this.variable('four'), 4) 13 | 14 | const value = this.variable('value') 15 | assert.equal(value.more.last, 1338) 16 | assert.equal(value.c, 1) 17 | 18 | const empty = this.variable('empty') 19 | assert.equal(empty[12], 2) 20 | 21 | assert.deepEqual(this.userVariable('global'), { test: 'test' }) 22 | 23 | const other2 = this.variable('other2') 24 | assert.equal(other2.data.metadata.text, 4) 25 | }) 26 | .end() 27 | } -------------------------------------------------------------------------------- /tests/interpreter/output.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | > hello 4 | > you have nb message [5] 5 | message = 3 6 | > you have nb message [message] 7 | 8 | } -------------------------------------------------------------------------------- /tests/interpreter/output.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function(user, assert, converse) { 2 | 3 | converse.configure({ 4 | languages: { 5 | path: __dirname + '/../languages', 6 | packages: ['en_EN'] 7 | } 8 | }).load() 9 | 10 | return user 11 | .start() 12 | .spy('start', function() { 13 | const output = this.output() 14 | assert.equal(output[0], 'Hello world') 15 | assert.equal(output[1], 'You have 5 messages') 16 | assert.equal(output[2], 'You have 3 messages') 17 | }) 18 | .end() 19 | } -------------------------------------------------------------------------------- /tests/interpreter/request.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | > get 4 | Request('https://jsonplaceholder.typicode.com/users') 5 | > thanks 6 | } -------------------------------------------------------------------------------- /tests/interpreter/request.spec.js: -------------------------------------------------------------------------------- 1 | const Request = require('../../functions/request') 2 | 3 | module.exports = function(user, assert, converse) { 4 | 5 | converse.functions({ 6 | Request 7 | }) 8 | 9 | converse.mock('Request', function(url) { 10 | return { 11 | statusCode: 200 12 | } 13 | }) 14 | 15 | return user 16 | .start() 17 | .spy('start', function() { 18 | assert.deepEqual(this.output(), ['get', 'thanks']) 19 | const { statusCode } = this.magicVariable('response') 20 | assert.equal(statusCode, 200) 21 | }) 22 | .end() 23 | } -------------------------------------------------------------------------------- /tests/interpreter/trigger-event.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | 4 | } 5 | 6 | @Event('on', 'broadcast') 7 | broadcast() { 8 | > ok 9 | } 10 | 11 | @Event('on', 'fake') 12 | fake() { 13 | > fake 14 | } -------------------------------------------------------------------------------- /tests/interpreter/trigger-event.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function (user, assert, converse) { 2 | return user 3 | .event('broadcast', function () { 4 | const output = this.output() 5 | assert.equal(output.length, 1) 6 | assert.equal(output[0], 'ok') 7 | }) 8 | .end() 9 | } -------------------------------------------------------------------------------- /tests/interpreter/variable.converse: -------------------------------------------------------------------------------- 1 | $score = 0 2 | 3 | @Event('start') 4 | start() { 5 | $score++ 6 | Input() 7 | $score += 3 8 | > your score {$score} 9 | } -------------------------------------------------------------------------------- /tests/interpreter/variable.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function(user, assert) { 2 | return user 3 | .start(function() { 4 | assert.equal(this.userVariable('score'), 1) 5 | }) 6 | .input('sam', function() { 7 | const [output] = this.output() 8 | assert.equal(output, 'your score 4') 9 | assert.equal(this.userVariable('score'), 4) 10 | }) 11 | .end() 12 | } -------------------------------------------------------------------------------- /tests/interpreter/while.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | val = 0 4 | 5 | while (val < 3) { 6 | > {val} 7 | Input() 8 | val++ 9 | } 10 | 11 | } 12 | 13 | -------------------------------------------------------------------------------- /tests/interpreter/while.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function(user, assert, converse) { 2 | return user 3 | .start(function() { 4 | assert.deepEqual(this.output(), [ '0' ]) 5 | assert.equal(this.variable('val'), 0) 6 | }) 7 | .spy('start', function() { 8 | assert.equal(this.variable('val'), 3) 9 | }) 10 | .input('one', function() { 11 | assert.deepEqual(this.output(), [ '1' ]) 12 | assert.equal(this.variable('val'), 1) 13 | }) 14 | .input('two', function() { 15 | assert.deepEqual(this.output(), [ '2' ]) 16 | assert.equal(this.variable('val'), 2) 17 | }) 18 | .input('three', testing => { 19 | assert.equal(testing.variable('val'), 3) 20 | }) 21 | .end() 22 | } 23 | -------------------------------------------------------------------------------- /tests/interpreter/wit.converse: -------------------------------------------------------------------------------- 1 | @Event('start') 2 | start() { 3 | 4 | } 5 | 6 | @Intent('departure') 7 | search() { 8 | > Your departure : {:intent.city} 9 | } -------------------------------------------------------------------------------- /tests/interpreter/wit.spec.js: -------------------------------------------------------------------------------- 1 | module.exports = function (user, assert, converse) { 2 | 3 | converse.configure({ 4 | 'wit.ai': { 5 | token: 'RNDPYV3RMC3BZ3EANRP7MPCYBA6N3GZM' 6 | } 7 | }) 8 | 9 | converse.nlp('wit.ai', { 10 | departure(string, structured) { 11 | const { location } = structured.entities 12 | return { city: location[0].value } 13 | } 14 | }) 15 | 16 | converse.mockNlp('wit.ai', function () { 17 | return { 18 | entities: { 19 | location: [ 20 | { 21 | value: 'Paris' 22 | } 23 | ] 24 | } 25 | } 26 | }) 27 | 28 | converse.useNlp('wit.ai') 29 | 30 | return user 31 | .start() 32 | .input('je pars a Paris') 33 | .spy('search', function () { 34 | const [output] = this.output() 35 | assert.equal(output, 'Your departure : Paris') 36 | }) 37 | .end() 38 | } -------------------------------------------------------------------------------- /tests/interpreter2/block.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { 3 | ConverseTesting, 4 | bot, 5 | user 6 | } = require('../../testing') 7 | 8 | describe('Block Spec', () => { 9 | let converse, userConverse 10 | 11 | function code(str) { 12 | converse = new ConverseTesting({ 13 | code: str, 14 | functions: { 15 | calc() { 16 | return 1+1 17 | } 18 | } 19 | }) 20 | userConverse = converse.createUser() 21 | } 22 | 23 | it('Function in Block', () => { 24 | code(` 25 | @Event('start') 26 | start() { 27 | if (true) { 28 | nb = calc() 29 | > { nb } 30 | } 31 | } 32 | `) 33 | return userConverse 34 | .conversation( 35 | bot `2` 36 | ) 37 | }) 38 | 39 | }) 40 | -------------------------------------------------------------------------------- /tests/interpreter2/condition-decorator.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { ConverseTesting } = require('../../index') 3 | 4 | describe('condition decorator', () => { 5 | let converse, user 6 | 7 | function code(str) { 8 | converse = new ConverseTesting() 9 | converse.code(str) 10 | user = converse.createUser({ 11 | session: 111 12 | }) 13 | } 14 | 15 | it('test condition decorator', () => { 16 | code(` 17 | @Condition('fn') 18 | @Event('start') 19 | start() { 20 | > Yo ? 21 | } 22 | 23 | @Event('nothing') 24 | nothing() { 25 | > Nop 26 | } 27 | `) 28 | converse.conditions({ 29 | fn(data, user) { 30 | return data.session != 111 31 | } 32 | }) 33 | return user 34 | .start(testing => { 35 | assert.equal(testing.output(0), 'Nop') 36 | }) 37 | .end() 38 | }) 39 | 40 | it('test condition decorator ands intent', () => { 41 | code(` 42 | @Condition('fn') 43 | @Event('start') 44 | start() { 45 | > Yo ? 46 | } 47 | 48 | @Intent(/hey/i) 49 | hey() { 50 | > hey 51 | } 52 | 53 | @Event('nothing') 54 | nothing() { 55 | > Nop 56 | } 57 | `) 58 | converse.conditions({ 59 | fn(data, user) { 60 | return false 61 | } 62 | }) 63 | return user 64 | .prompt('hey', testing => { 65 | assert.equal(testing.output(0), 'hey') 66 | }) 67 | .end() 68 | }) 69 | 70 | }) -------------------------------------------------------------------------------- /tests/interpreter2/constants.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { 3 | ConverseTesting 4 | } = require('../../index') 5 | 6 | describe('Params Test', () => { 7 | let converse, user 8 | 9 | function code(str) { 10 | converse = new ConverseTesting() 11 | converse.code(str) 12 | user = converse.createUser() 13 | } 14 | 15 | it('Constant', () => { 16 | code(` 17 | @Event('start') 18 | start() { 19 | > { BASE_URL } 20 | } 21 | `) 22 | 23 | converse.constants({ 24 | BASE_URL: 'newbot.io' 25 | }) 26 | 27 | user 28 | .start(testing => { 29 | assert.equal(testing.output(0), 'newbot.io') 30 | }) 31 | .end() 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /tests/interpreter2/context.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { ConverseTesting } = require('../../index') 3 | 4 | describe('Context Test', () => { 5 | 6 | let converse, user 7 | 8 | function code(str) { 9 | converse = new ConverseTesting() 10 | converse.code(str) 11 | user = converse.createUser() 12 | } 13 | 14 | it('if user change conversation', () => { 15 | code(` 16 | @Event('start') 17 | start() { 18 | > Your name ? 19 | Prompt() 20 | > Ok {:text} 21 | } 22 | 23 | @Intent('hello') 24 | hello() { 25 | > Hey 26 | } 27 | `) 28 | converse.nlp('regexp', { 29 | hello(str) { 30 | const match = str.match(/hello/) 31 | if (!match) return false 32 | return true 33 | } 34 | }) 35 | user 36 | .start(testing => { 37 | assert.equal(testing.output(0), 'Your name ?') 38 | }) 39 | .prompt('hello', testing => { 40 | const output = testing.output() 41 | assert.equal(output.length, 1) 42 | assert.equal(output[0], 'Hey') 43 | }) 44 | .prompt('sam', testing => { 45 | const output = testing.output() 46 | }) 47 | .end() 48 | }) 49 | 50 | }) -------------------------------------------------------------------------------- /tests/interpreter2/else.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { 3 | ConverseTesting, 4 | bot, 5 | user 6 | } = require('../../testing') 7 | 8 | describe('Else', () => { 9 | let converse, userConverse 10 | 11 | function code(str) { 12 | converse = new ConverseTesting() 13 | converse.code(str) 14 | userConverse = converse.createUser() 15 | } 16 | 17 | it('Else Block', () => { 18 | code(` 19 | @Event('start') 20 | start() { 21 | a = 10 22 | if (a == 1) { 23 | > Noop 24 | } 25 | else { 26 | > Yeh 27 | } 28 | } 29 | `) 30 | return userConverse 31 | .conversation( 32 | bot `Yeh` 33 | ) 34 | }) 35 | 36 | it('Else if Block', () => { 37 | code(` 38 | @Event('start') 39 | start() { 40 | a = 10 41 | if (a == 1) { 42 | > Noop 43 | } 44 | else if (a > 5) { 45 | > Cool 46 | } 47 | else { 48 | > Yeh 49 | } 50 | } 51 | `) 52 | return userConverse 53 | .conversation( 54 | bot `Cool` 55 | ) 56 | }) 57 | 58 | it('Else with Prompt()', () => { 59 | code(` 60 | @Event('start') 61 | start() { 62 | if (2 == 1) { 63 | > a 64 | } 65 | else { 66 | if (1 == 1) { 67 | if (2 == 1) { 68 | > c 69 | } 70 | else { 71 | Prompt() 72 | > b 73 | } 74 | } 75 | } 76 | } 77 | `) 78 | return userConverse 79 | .start() 80 | .prompt('test', testing => { 81 | assert.equal(testing.output(0), 'b') 82 | }) 83 | .end() 84 | }) 85 | 86 | }) 87 | -------------------------------------------------------------------------------- /tests/interpreter2/eval.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { ConverseTesting } = require('../../index') 3 | 4 | describe('Eval Test', () => { 5 | let converse, user 6 | 7 | function code(str) { 8 | converse = new ConverseTesting() 9 | converse.code(str) 10 | user = converse.createUser() 11 | } 12 | 13 | it('eval json converse', () => { 14 | code(` 15 | @Event('start') 16 | start() { 17 | > One 18 | Eval.json(\` 19 | [ 20 | { "output": "Two" } 21 | ] 22 | \`) 23 | > Three 24 | } 25 | `) 26 | user 27 | .start(testing => { 28 | assert.deepEqual(testing.output(), [ 29 | 'One', 30 | 'Two', 31 | 'Three' 32 | ]) 33 | }) 34 | .end() 35 | }) 36 | 37 | it('eval json converse with prompt', () => { 38 | code(` 39 | @Event('start') 40 | start() { 41 | > Your name ? 42 | Eval.json(\` 43 | [ 44 | { 45 | "type": "executeFn", 46 | "name": "Prompt" 47 | } 48 | ] 49 | \`) 50 | > Thanks ! 51 | } 52 | `) 53 | user 54 | .start(testing => { 55 | assert.equal(testing.output().length, 1) 56 | assert.equal(testing.output(0), 'Your name ?') 57 | }) 58 | /*.prompt('sam', testing => { 59 | assert.equal(testing.output(0), 'Thanks !') 60 | })*/ 61 | .end() 62 | }) 63 | 64 | }) -------------------------------------------------------------------------------- /tests/interpreter2/event.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { ConverseTesting } = require('../../index') 3 | 4 | describe('Event Test', () => { 5 | let converse, user 6 | 7 | function code(str) { 8 | converse = new ConverseTesting() 9 | converse.code(str) 10 | user = converse.createUser() 11 | } 12 | 13 | it('call event', () => { 14 | code(` 15 | @Event('on', 'custom') 16 | custom() { 17 | > Hello 18 | } 19 | `) 20 | user 21 | .event('custom', testing => { 22 | assert.equal(testing.output(0), 'Hello') 23 | }) 24 | .end() 25 | }) 26 | 27 | it('call event with intent', () => { 28 | code(` 29 | @Event('on', 'custom') 30 | test() { 31 | > Hello 32 | } 33 | 34 | @Intent(/custom/) 35 | custom() { 36 | > Other 37 | } 38 | `) 39 | user 40 | .event('custom', testing => { 41 | assert.equal(testing.output(0), 'Hello') 42 | }) 43 | .end() 44 | }) 45 | }) -------------------------------------------------------------------------------- /tests/interpreter2/format.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { ConverseTesting } = require('../../index') 3 | 4 | describe('Format Test', () => { 5 | let converse, user 6 | 7 | function code(str, options = {}) { 8 | converse = new ConverseTesting({ 9 | code: str, 10 | formats: { 11 | quickReplies(text, [actions]) { 12 | return { 13 | text, 14 | actions 15 | } 16 | }, 17 | image(text, [image]) { 18 | return { 19 | text, 20 | image 21 | } 22 | }, 23 | smiley(str) { 24 | return 'oo' 25 | } 26 | }, 27 | ...options 28 | }) 29 | user = converse.createUser() 30 | } 31 | 32 | it('test multi format', () => { 33 | code(` 34 | @Event('start') 35 | start() { 36 | @Format('image', 'test.com') 37 | @Format('quickReplies', ['yes', 'no']) 38 | > test 39 | } 40 | `) 41 | user 42 | .start(testing => { 43 | const obj = testing.output(0) 44 | assert.deepStrictEqual(obj, { 45 | image: { text: 'test', image: 'test.com' }, 46 | quickReplies: { 47 | text: { text: 'test', image: 'test.com' }, 48 | actions: [ 'yes', 'no' ] 49 | } 50 | }) 51 | }) 52 | .end() 53 | }) 54 | 55 | it('merge multi format', () => { 56 | code(` 57 | @Event('start') 58 | start() { 59 | @Format('image', 'test.com') 60 | @Format('quickReplies', ['yes', 'no']) 61 | > test 62 | } 63 | `, { 64 | mergeMultiFormats: true 65 | }) 66 | user 67 | .start(testing => { 68 | const obj = testing.output(0) 69 | assert.deepStrictEqual(obj, { text: 'test', actions: [ 'yes', 'no' ], image: 'test.com' }) 70 | }) 71 | .end() 72 | }) 73 | 74 | }) -------------------------------------------------------------------------------- /tests/interpreter2/intent.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { 3 | ConverseTesting 4 | } = require('../../testing') 5 | 6 | describe('Get Intent Params', () => { 7 | let converse, userConverse 8 | 9 | function code(str) { 10 | converse = new ConverseTesting({ 11 | skills: { 12 | child: { 13 | code: ` 14 | @Intent('child', [ 15 | 'ho child', 16 | 'get child' 17 | ]) 18 | Child() { 19 | > Child 20 | } 21 | ` 22 | } 23 | } 24 | }) 25 | converse.code(str) 26 | userConverse = converse.createUser() 27 | } 28 | 29 | it('params in intent', () => { 30 | code(` 31 | @Intent('greetings', [ 32 | 'hey', 33 | 'hello' 34 | ]) 35 | hey() { 36 | > Hey 37 | } 38 | 39 | @Intent('bye', [ 40 | 'bye', 41 | 'good bye' 42 | ]) 43 | bye() { 44 | > Ok 45 | } 46 | `) 47 | converse.getAllIntents().then((intents) => { 48 | assert.equal(intents.length, 3) 49 | assert.equal(intents[0].params[0], 'greetings') 50 | }) 51 | }) 52 | 53 | }) 54 | -------------------------------------------------------------------------------- /tests/interpreter2/loop.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { 3 | ConverseTesting, 4 | bot, 5 | user 6 | } = require('../../testing') 7 | 8 | describe('Loop', () => { 9 | let converse, userConverse 10 | 11 | function code(str) { 12 | converse = new ConverseTesting() 13 | converse.code(str) 14 | userConverse = converse.createUser() 15 | } 16 | 17 | it('for of with array', () => { 18 | code(` 19 | @Event('start') 20 | start() { 21 | names = ['sam', 'jim'] 22 | for (name of names) { 23 | > { name } 24 | } 25 | } 26 | `) 27 | return userConverse.conversation( 28 | bot `sam`, 29 | bot `jim` 30 | ) 31 | }) 32 | 33 | it('for of with number', () => { 34 | code(` 35 | @Event('start') 36 | start() { 37 | for (nb of 1) { 38 | > { nb } 39 | } 40 | } 41 | `) 42 | return userConverse.conversation( 43 | bot `0`, 44 | bot `1` 45 | ) 46 | }) 47 | 48 | it('for of with object', () => { 49 | code(` 50 | @Event('start') 51 | start() { 52 | obj = { a: 'sam', b: 'jim' } 53 | for (name of obj) { 54 | > { name } 55 | } 56 | } 57 | `) 58 | return userConverse.conversation( 59 | bot `sam`, 60 | bot `jim` 61 | ) 62 | }) 63 | 64 | it('for of with prompt', () => { 65 | code(` 66 | @Event('start') 67 | start() { 68 | names = ['sam', 'jim'] 69 | for (name of names) { 70 | > { name } 71 | Prompt() 72 | } 73 | } 74 | `) 75 | return userConverse.conversation( 76 | bot `sam`, 77 | user `test`, 78 | bot `jim`, 79 | user `test` 80 | ) 81 | }) 82 | 83 | it('for of with function', () => { 84 | code(` 85 | @Event('start') 86 | start() { 87 | > Go 88 | names = ['sam', 'jim'] 89 | for (name of names) { 90 | Prompt() 91 | index = Array.indexOf(names, :text) 92 | > { index } 93 | } 94 | } 95 | `) 96 | return userConverse.conversation( 97 | bot `Go`, 98 | user `sam`, 99 | bot `0`, 100 | user `jim`, 101 | bot `1` 102 | ) 103 | }) 104 | 105 | }) 106 | -------------------------------------------------------------------------------- /tests/interpreter2/multi-skill.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { 3 | ConverseTesting 4 | } = require('../../index') 5 | 6 | describe('Multi Skill', () => { 7 | let converse, user 8 | 9 | async function code(str, platform = 'website') { 10 | converse = new ConverseTesting({ 11 | code: str, 12 | skills: { 13 | helloSkill: [ 14 | { 15 | skill: ` 16 | displayProfile() { 17 | > Call profile API in Google Actions 18 | } 19 | `, 20 | condition(data, user) { 21 | return data.session.platform == "gactions"; 22 | } 23 | }, 24 | { 25 | skill: ` 26 | displayProfile() { 27 | > Call profile API in Amazon Alexa 28 | } 29 | `, 30 | condition(data, user) { 31 | return data.session.platform == "alexa"; 32 | } 33 | }, 34 | { 35 | skill: ` 36 | displayProfile() { 37 | > Default response 38 | } 39 | ` 40 | } 41 | ] 42 | } 43 | }, { 44 | loadSkills: false 45 | }) 46 | await converse.loadSkills() 47 | user = converse.createUser({ 48 | session: { 49 | platform 50 | } 51 | }) 52 | } 53 | 54 | it('Test condition() in multi skills, default response', async () => { 55 | await code(` 56 | @Event('start') 57 | start() { 58 | helloSkill.displayProfile() 59 | } 60 | `) 61 | 62 | return user 63 | .start(testing => { 64 | assert.equal(testing.output(0), 'Default response') 65 | }) 66 | .end() 67 | }) 68 | 69 | it('Test condition() in multi skills, Gactions response', async () => { 70 | await code(` 71 | @Event('start') 72 | start() { 73 | helloSkill.displayProfile() 74 | } 75 | `, 'gactions') 76 | 77 | return user 78 | .start(testing => { 79 | assert.equal(testing.output(0), 'Call profile API in Google Actions') 80 | }) 81 | .end() 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /tests/interpreter2/native-nlp.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { ConverseTesting } = require('../../index') 3 | const { train } = require('../../packages/train') 4 | const { LangFr } = require('@nlpjs/lang-fr'); 5 | const { LangEn } = require('@nlpjs/lang-en'); 6 | 7 | 8 | describe('Test Native NLP', () => { 9 | let converse, user 10 | 11 | async function code(str, options = {}) { 12 | converse = new ConverseTesting({ 13 | code: str, 14 | ...options 15 | }) 16 | const langs = [LangFr, LangEn] 17 | const model = await train(converse, langs) 18 | await converse.setModelNlp(model, langs) 19 | user = converse.createUser() 20 | return model 21 | } 22 | 23 | 24 | it('Test native NLP', async () => { 25 | await code(` 26 | @Intent('hello', [ 27 | 'hello', 28 | 'yo' 29 | ]) 30 | hello() { 31 | > Hey 32 | } 33 | `) 34 | return user 35 | .prompt('hello', testing => { 36 | const output = testing.output() 37 | assert.equal(output.length, 1) 38 | assert.equal(output[0], 'Hey') 39 | }) 40 | .end() 41 | }) 42 | 43 | it('Extract Entities', async () => { 44 | const model = await code(` 45 | @Intent('room', [ 46 | 'get room tomorrow' 47 | ]) 48 | room() { 49 | > Ok 50 | } 51 | `) 52 | return user 53 | .prompt('get room today', testing => { 54 | const intent = testing.magicVariable('intent') 55 | const entity = testing.magicVariable('entity') 56 | assert.ok(entity.date) 57 | assert.equal(intent.entities[0].entity, 'date') 58 | }) 59 | .end() 60 | }) 61 | 62 | it('Other language', async () => { 63 | await code(` 64 | @Intent('hello', { 65 | fr: [ 66 | 'bonjour', 67 | 'nous allons manger' 68 | ] 69 | }) 70 | hello() { 71 | > Hey 72 | } 73 | `) 74 | console.time('one') 75 | return user 76 | .prompt('je mange', testing => { 77 | const output = testing.output() 78 | assert.equal(output.length, 1) 79 | assert.equal(output[0], 'Hey') 80 | }) 81 | .end() 82 | .then(_ => { 83 | console.timeEnd('one') 84 | }) 85 | }) 86 | 87 | it('Other language, identifier', async () => { 88 | await code(` 89 | @Intent('hello', [ 90 | '#hello' 91 | ]) 92 | hello() { 93 | > Hey 94 | } 95 | `, { 96 | languages: { 97 | packages: { 98 | fr_FR: [ 99 | { 100 | 'hello': 'Hello World' 101 | } 102 | ] 103 | } 104 | } 105 | }) 106 | return user 107 | .prompt('world', testing => { 108 | const output = testing.output() 109 | assert.equal(output.length, 1) 110 | assert.equal(output[0], 'Hey') 111 | }) 112 | .end() 113 | }) 114 | 115 | it('Child skill', async () => { 116 | await code('', { 117 | skills: { 118 | child: { 119 | code: ` 120 | @Intent('hello', [ 121 | 'hello', 122 | 'yo' 123 | ]) 124 | hello() { 125 | > Hey 126 | } 127 | ` 128 | } 129 | } 130 | }) 131 | return user 132 | .prompt('hello', testing => { 133 | const output = testing.output() 134 | assert.equal(output.length, 1) 135 | assert.equal(output[0], 'Hey') 136 | }) 137 | .end() 138 | }) 139 | 140 | it('Not intent found', async () => { 141 | await code(` 142 | @Event('start') 143 | start() { 144 | > Hey 145 | } 146 | `) 147 | return user 148 | .prompt('hello', testing => { 149 | const output = testing.output() 150 | assert.equal(output.length, 1) 151 | assert.equal(output[0], 'Hey') 152 | }) 153 | .end() 154 | }) 155 | 156 | }) -------------------------------------------------------------------------------- /tests/interpreter2/params.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { ConverseTesting } = require('../../index') 3 | 4 | describe('Params Test', () => { 5 | let converse, user 6 | 7 | function code(str) { 8 | converse = new ConverseTesting() 9 | converse.code(str) 10 | user = converse.createUser() 11 | } 12 | 13 | it('array params exec', () => { 14 | code(` 15 | @Event('start') 16 | start() { 17 | > Hello 18 | Prompt() 19 | fn([:text]) 20 | } 21 | fn(a) { 22 | > {a[0]} 23 | } 24 | `) 25 | user 26 | .start(testing => { 27 | assert.equal(testing.output(0), 'Hello') 28 | }) 29 | .prompt('Foo', testing => { 30 | assert.equal(testing.output(0), 'Foo') 31 | }) 32 | .end() 33 | }) 34 | 35 | it('deep array params exec', () => { 36 | code(` 37 | @Event('start') 38 | start() { 39 | > Hello 40 | Prompt() 41 | fn([[[:text]]]) 42 | } 43 | fn(a) { 44 | > {a[0][0][0]} 45 | } 46 | `) 47 | user 48 | .start(testing => { 49 | assert.equal(testing.output(0), 'Hello') 50 | }) 51 | .prompt('Foo', testing => { 52 | assert.equal(testing.output(0), 'Foo') 53 | }) 54 | .end() 55 | }) 56 | 57 | it('params optional', () => { 58 | code(` 59 | @Event('start') 60 | start() { 61 | > Hello 62 | Prompt() 63 | fn() 64 | } 65 | fn(a) { 66 | > {a} 67 | } 68 | `) 69 | user 70 | .start(testing => { 71 | assert.equal(testing.output(0), 'Hello') 72 | }) 73 | .prompt('Foo', testing => { 74 | assert.equal(testing.output(0), '') 75 | }) 76 | .end() 77 | }) 78 | 79 | it('params optional 2', () => { 80 | code(` 81 | @Event('start') 82 | start() { 83 | > Hello 84 | Prompt() 85 | fn('a', 'b') 86 | } 87 | fn(a) { 88 | > {a} 89 | } 90 | `) 91 | user 92 | .start(testing => { 93 | assert.equal(testing.output(0), 'Hello') 94 | }) 95 | .prompt('Foo', testing => { 96 | assert.equal(testing.output(0), 'a') 97 | }) 98 | .end() 99 | }) 100 | }) -------------------------------------------------------------------------------- /tests/interpreter2/pause.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { 3 | ConverseTesting, 4 | bot, 5 | user 6 | } = require('../../testing') 7 | 8 | describe('Pause script', () => { 9 | let converse, userConverse 10 | 11 | function code(str) { 12 | converse = new ConverseTesting({ 13 | code: str 14 | }) 15 | userConverse = converse.createUser() 16 | } 17 | 18 | it('test Pause function', () => { 19 | code(` 20 | @Event('start') 21 | start() { 22 | > Hello 23 | Pause() 24 | > Thanks 25 | } 26 | `) 27 | return userConverse 28 | .start(testing => { 29 | assert.equal(testing.output(0), 'Hello') 30 | }) 31 | .continue(testing => { 32 | assert.equal(testing.output(0), 'Thanks') 33 | }) 34 | .end() 35 | }) 36 | 37 | it('test Pause function and get value', () => { 38 | code(` 39 | @Event('start') 40 | start() { 41 | > Hello 42 | Pause() 43 | > Thanks 44 | } 45 | `) 46 | return userConverse 47 | .start(testing => { 48 | assert.equal(testing.output(0), 'Hello') 49 | }) 50 | .continue({ foo: 'test' }, testing => { 51 | const event = testing.magicVariable('event') 52 | assert.equal(event.foo, 'test') 53 | assert.equal(testing.output(0), 'Thanks') 54 | }) 55 | .end() 56 | }) 57 | }) -------------------------------------------------------------------------------- /tests/interpreter2/regexp.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { ConverseTesting } = require('../../index') 3 | 4 | describe('RegExp Test', () => { 5 | let converse, user 6 | 7 | function code(str) { 8 | converse = new ConverseTesting() 9 | converse.code(str) 10 | user = converse.createUser() 11 | } 12 | 13 | it('intent regexp', () => { 14 | code(` 15 | @Intent(/hello/) 16 | intent() { 17 | a = { 18 | b: /a/ 19 | } 20 | if (a.b.test('a')) > Hello 21 | } 22 | `) 23 | user 24 | .start() 25 | .prompt('hello', testing => { 26 | assert.equal(testing.output(0), 'Hello') 27 | }) 28 | .end() 29 | }) 30 | }) -------------------------------------------------------------------------------- /tests/interpreter2/return.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { 3 | ConverseTesting, 4 | bot, 5 | user 6 | } = require('../../testing') 7 | 8 | describe('Params Test', () => { 9 | let converse, userConverse 10 | 11 | function code(str) { 12 | converse = new ConverseTesting() 13 | converse.code(str) 14 | userConverse = converse.createUser() 15 | } 16 | 17 | it('Test return level 1', () => { 18 | code(` 19 | @Event('start') 20 | start() { 21 | > Stop 22 | return false 23 | > Hello 24 | } 25 | `) 26 | return userConverse.start() 27 | .spy('start', testing => { 28 | assert.deepEqual(testing.output(), ['Stop']) 29 | }) 30 | .end() 31 | }) 32 | 33 | it('Test return level 2', () => { 34 | code(` 35 | @Event('start') 36 | start() { 37 | (true) { 38 | > Stop 39 | return false 40 | } 41 | > Hello 42 | } 43 | @Event('nothing') 44 | nothing() { 45 | > Noop 46 | } 47 | `) 48 | return userConverse.start() 49 | .spy('start', testing => { 50 | assert.deepEqual(testing.output(), ['Stop']) 51 | }) 52 | .end() 53 | }) 54 | 55 | it('Test return deep level', () => { 56 | code(` 57 | @Event('start') 58 | start() { 59 | (true) { 60 | > A 61 | (true) { 62 | > B 63 | (true) { 64 | > Stop 65 | return false 66 | } 67 | } 68 | } 69 | > Hello 70 | } 71 | `) 72 | return userConverse.start() 73 | .spy('start', testing => { 74 | assert.deepEqual(testing.output(), ['A', 'B', 'Stop']) 75 | }) 76 | .end() 77 | }) 78 | 79 | it('Return value', () => { 80 | code(` 81 | @Event('start') 82 | start() { 83 | val = math(1) 84 | > { val } 85 | } 86 | 87 | math(a) { 88 | return a + :text 89 | } 90 | `) 91 | return userConverse.conversation( 92 | user `2`, 93 | bot `3` 94 | ) 95 | }) 96 | 97 | 98 | 99 | it('Return value with Prompt', () => { 100 | code(` 101 | @Event('start') 102 | start() { 103 | val = math(1) + 2 104 | > { val } 105 | } 106 | 107 | math(a) { 108 | > enter a number 109 | Prompt() 110 | return a + :text 111 | } 112 | `) 113 | return userConverse.conversation( 114 | user `start`, 115 | bot `enter a number`, 116 | user `3`, 117 | bot `6` 118 | ) 119 | }) 120 | 121 | it('Return value with Prompt but parent-child relation', () => { 122 | 123 | code(` 124 | @Event('start') 125 | start() { 126 | ret = child.test() 127 | > { ret } 128 | } 129 | `) 130 | 131 | converse.setSkills({ 132 | child: { 133 | code: ` 134 | test() { 135 | Prompt() 136 | return :text 137 | } 138 | ` 139 | } 140 | }) 141 | 142 | return userConverse.conversation( 143 | user `start`, 144 | user `yo`, 145 | bot `yo` 146 | ) 147 | }) 148 | 149 | 150 | 151 | it('Return value other function', () => { 152 | code(` 153 | @Event('start') 154 | start() { 155 | val = foo(2) 156 | > { val } 157 | } 158 | 159 | foo(a) { 160 | return a + math(3) 161 | } 162 | 163 | math(b) { 164 | Prompt() 165 | return b + :text 166 | } 167 | `) 168 | return userConverse.conversation( 169 | user `go`, 170 | user `2`, 171 | bot `7` 172 | ) 173 | }) 174 | 175 | }) 176 | -------------------------------------------------------------------------------- /tests/interpreter2/translate.spec.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const { ConverseTesting } = require('../../index') 3 | 4 | describe('Translate Test', () => { 5 | let converse, user 6 | 7 | function code(str) { 8 | converse = new ConverseTesting() 9 | converse.code(str) 10 | user = converse.createUser() 11 | } 12 | 13 | it('translation in string value', () => { 14 | code(` 15 | @Event('start') 16 | start() { 17 | str = '#discover' 18 | > { str } 19 | 20 | obj = { 21 | foo: '#hello' 22 | } 23 | > { obj.foo } 24 | 25 | array = ['#view'] 26 | > { array[0] } 27 | 28 | @Format('carousel', [ 29 | { 30 | buttons: [ 31 | { title: '#go' } 32 | ] 33 | } 34 | ]) 35 | > Ok 36 | } 37 | `) 38 | 39 | converse.configure({ 40 | languages: { 41 | path: __dirname + '/../languages', 42 | packages: ['fr_FR', 'en_EN'] 43 | } 44 | }).loadLanguage() 45 | 46 | converse.format('carousel', (str, [cards]) => { 47 | return cards[0].buttons[0].title 48 | }) 49 | 50 | user 51 | .start(testing => { 52 | assert.equal(testing.output(0), 'Découvrir') 53 | assert.equal(testing.output(1), 'Salut') 54 | assert.equal(testing.output(2), 'Voir') 55 | assert.equal(testing.output(3), 'Aller') 56 | }) 57 | .end() 58 | }) 59 | 60 | }) -------------------------------------------------------------------------------- /tests/languages/en_EN.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "hello": "hello world", 4 | "$you": { 5 | "you have": "you have" 6 | }, 7 | "$you nb message": "{1} %d message%p", 8 | "$you no message": "{1} no message" 9 | }, 10 | { 11 | "plurial": { 12 | "p": [ 13 | "s" 14 | ] 15 | } 16 | } 17 | ] -------------------------------------------------------------------------------- /tests/languages/fr_FR.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "hello": "salut", 4 | "discover": "découvrir", 5 | "view": "Voir", 6 | "go": "Aller" 7 | }, 8 | { 9 | "plurial": { 10 | "p": [ 11 | "s" 12 | ] 13 | } 14 | } 15 | ] -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es5", 5 | "sourceMap": true, 6 | "allowJs": true, 7 | "typeRoots": [ "./types" ] 8 | }, 9 | "exclude": [ 10 | "node_modules", 11 | "bin", 12 | "**/*.spec.js" 13 | ], 14 | "include": [ 15 | "src/**/*" 16 | ] 17 | } -------------------------------------------------------------------------------- /types/converse.ts: -------------------------------------------------------------------------------- 1 | import { User } from './user' 2 | 3 | type Output = { 4 | output: OutputFunction, 5 | data?: any, 6 | preUser?: (user: User, newbot?: INewBot) => any 7 | } 8 | 9 | type OutputFunction = (output: any, done: Function) => any 10 | 11 | export interface INewBot { 12 | exec?(message: string, userId: string|number, options?: OutputFunction|Output) 13 | exec?(message: string, options?: OutputFunction|Output) 14 | event?(name: string) 15 | event?(name: string, data: any) 16 | } -------------------------------------------------------------------------------- /types/index.ts: -------------------------------------------------------------------------------- 1 | import { Skill } from './skill' 2 | import { INewBot } from './converse' 3 | 4 | export { 5 | Skill, 6 | INewBot 7 | } -------------------------------------------------------------------------------- /types/skill.ts: -------------------------------------------------------------------------------- 1 | import { User } from './user' 2 | 3 | type Converse = {}; 4 | 5 | type Data = any; 6 | 7 | type NlpFunction = { 8 | [key: string]: ( 9 | text?: string, 10 | userId?: string | number, 11 | converse?: Converse 12 | ) => boolean | { [key: string]: any }; 13 | }; 14 | 15 | export interface Skill { 16 | /** code is */ 17 | code?: string; 18 | file?: string; 19 | /** 20 | * List of functions that can be used in the conversational script 21 | * 22 | * ```js 23 | * { 24 | * functions: { 25 | * foo() { 26 | * 27 | * } 28 | * } 29 | * } 30 | * 31 | * ``` 32 | * 33 | * The function can return any value or even a promise 34 | * 35 | * The conversational script is composed as follows 36 | * 37 | * ```ts 38 | * @Event('start') 39 | * start() { 40 | * ret = foo() 41 | * } 42 | * ``` 43 | * 44 | * https://newbot.io/en/docs/avanced/function.html 45 | * */ 46 | functions?: { [key: string]: (...args: any[]) => any | Promise }; 47 | skills?: { 48 | [key: string]: 49 | | Skill 50 | | Promise 51 | | { 52 | skill: Skill | Promise; 53 | params?: any[]; 54 | } 55 | | Array<{ 56 | skill: Skill | Promise; 57 | params?: any[]; 58 | condition: (data?: Data, user?: User) => boolean; 59 | }>; 60 | }; 61 | constants?: { [key: string]: any }; 62 | formats?: { 63 | [key: string]: (text?: string, params?: any, data?: Data) => any; 64 | }; 65 | shareFormats?: boolean; 66 | shareNlp?: boolean; 67 | propagateNlp?: boolean | string | string[]; 68 | propagateFormats?: boolean, 69 | languages?: { 70 | packages: { [key: string]: JSON }; 71 | }; 72 | nlp?: NlpFunction | { [key: string]: NlpFunction }; 73 | canActivated?: string[] 74 | } 75 | -------------------------------------------------------------------------------- /types/user.ts: -------------------------------------------------------------------------------- 1 | export type User = { 2 | getMagicVariable(name: string): any; 3 | setMagicVariable(name: string, value: any): void; 4 | getVariable(name: string): any; 5 | setVariable(name: string, value: any): void; 6 | setLang(lang: string): void; 7 | getLang(): string; 8 | toJson(): any; 9 | fromJson(): JSON; 10 | }; 11 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const CompressionPlugin = require('compression-webpack-plugin') 3 | 4 | module.exports = { 5 | entry: { 6 | newbot: './bootstrap/browser.js', 7 | 'newbot.with-parser': './bootstrap/browser-with-parser.js', 8 | 'newbot.with-nlp': './bootstrap/browser-with-nlp.js' 9 | }, 10 | output: { 11 | filename: '[name].min.js', 12 | path: path.resolve(__dirname, 'dist') 13 | }, 14 | node: { 15 | fs: 'empty' 16 | }, 17 | optimization: { 18 | minimize: true 19 | }, 20 | plugins: [ 21 | new CompressionPlugin() 22 | ] 23 | } 24 | --------------------------------------------------------------------------------