├── bin └── cli.js ├── test ├── Yes.csv ├── Twilio-Basic-Starter.zip ├── alexa_model │ └── model.json ├── ta_test.js └── test_assistant.json ├── lib ├── error.js ├── twilio-assistant │ ├── index.js │ ├── client.js │ └── initConfig.js ├── json_utility.js └── files.js ├── cmd ├── version.js ├── init.js ├── imports │ ├── index.js │ ├── alexa.js │ └── dialogFlow.js ├── list.js ├── delete.js ├── update.js ├── simulate.js ├── create.js ├── field-value.js ├── export.js └── help.js ├── .gitignore ├── example_config.js ├── check-version.js ├── package.json ├── index.js └── readme.md /bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // require 3 | require('../')() 4 | -------------------------------------------------------------------------------- /test/Yes.csv: -------------------------------------------------------------------------------- 1 | Yes,yes please,yeah,ok,okay,sure,I agree,yep,of course,certainly,definitely,yes,YES 2 | -------------------------------------------------------------------------------- /test/Twilio-Basic-Starter.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twilio/autopilot-cli/HEAD/test/Twilio-Basic-Starter.zip -------------------------------------------------------------------------------- /lib/error.js: -------------------------------------------------------------------------------- 1 | module.exports = (message, exit) => { 2 | console.error(message) 3 | // exit 4 | exit && process.exit(1) 5 | } 6 | -------------------------------------------------------------------------------- /cmd/version.js: -------------------------------------------------------------------------------- 1 | const { version } = require('../package.json') 2 | 3 | module.exports = (args) => { 4 | console.log(`v${version}`) 5 | } -------------------------------------------------------------------------------- /lib/twilio-assistant/index.js: -------------------------------------------------------------------------------- 1 | const initConfig = require('./initConfig'); 2 | 3 | module.exports = Object.assign({}, 4 | initConfig 5 | ); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # project files and folders to ignore 2 | node_modules 3 | _dev 4 | _tmp 5 | data 6 | config.js 7 | config-*.js 8 | test_sample.js 9 | 10 | # other stuff to ignore 11 | .DS_Store 12 | Thumbs.db 13 | .idea 14 | .vscode -------------------------------------------------------------------------------- /example_config.js: -------------------------------------------------------------------------------- 1 | const config = {}; 2 | 3 | // twilio config 4 | config.twilio = {}; 5 | config.twilio.accountSid = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 6 | config.twilio.authToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 7 | 8 | module.exports = config; -------------------------------------------------------------------------------- /cmd/init.js: -------------------------------------------------------------------------------- 1 | const ta = require('../lib/twilio-assistant'); 2 | 3 | module.exports = async (args) => { 4 | 5 | if (args.hasOwnProperty('credentials') && args.credentials === true) { 6 | console.log(`The '--credentials ' arguments are required`) 7 | return 8 | } 9 | 10 | try { 11 | 12 | ta.initConfig(args); 13 | 14 | } catch (err) { 15 | 16 | console.error(err) 17 | } 18 | } -------------------------------------------------------------------------------- /cmd/imports/index.js: -------------------------------------------------------------------------------- 1 | const minimist = require('minimist') 2 | const error = require('../../lib/error') 3 | 4 | module.exports = () => { 5 | 6 | //TODO: check to see if CLI is configured 7 | 8 | const args = minimist(process.argv.slice(2)) 9 | 10 | let cmd = args._[1] || 'dialogFlow' 11 | 12 | if (args.help || args.h) { 13 | cmd = 'help' 14 | } 15 | 16 | switch (cmd) { 17 | 18 | case 'dialogFlow': 19 | require('./dialogFlow')(args) 20 | break 21 | 22 | case 'alexa': 23 | require('./alexa')(args) 24 | break 25 | 26 | case 'help': 27 | require('./cmd/help')(args) 28 | break 29 | 30 | default: 31 | error(`"${cmd}" is not a valid command!`, true) 32 | break 33 | } 34 | } -------------------------------------------------------------------------------- /cmd/list.js: -------------------------------------------------------------------------------- 1 | const AutopilotCore = require('@dabblelab/autopilot-core'), 2 | ora = require('ora'); 3 | 4 | module.exports = async (args) => { 5 | 6 | const spinner = await ora(); 7 | 8 | try { 9 | const profile = args.credentials || "default", 10 | twilioClient = await require('../lib/twilio-assistant/client')(profile); 11 | 12 | spinner.start('Getting assistants...\n') 13 | const assistants = await AutopilotCore.listAssistant(twilioClient); 14 | 15 | spinner.stop(); 16 | 17 | for( let i = 0 ; i < assistants.length ; i++){ 18 | console.log(`${assistants[i].sid} ${assistants[i].uniqueName}`); 19 | } 20 | 21 | 22 | } catch (err) { 23 | 24 | spinner.stop(); 25 | console.error(`ERROR: ${err}`); 26 | } 27 | } -------------------------------------------------------------------------------- /cmd/delete.js: -------------------------------------------------------------------------------- 1 | const ora = require('ora'), 2 | AutopilotCore = require('@dabblelab/autopilot-core'); 3 | 4 | module.exports = async (args) => { 5 | if (!args.hasOwnProperty('assistant')) { 6 | console.log(`The '--assistant' argument is required`); 7 | return; 8 | } 9 | 10 | const spinner = ora(); 11 | 12 | try { 13 | 14 | const sid = args.assistant, 15 | profile = args.credentials || "default", 16 | twilioClient = await require('../lib/twilio-assistant/client')(profile); 17 | 18 | spinner.start('Deleting assistant...'); 19 | await AutopilotCore.exportAssistant(sid, twilioClient, true); 20 | await AutopilotCore.deleteAssistant(sid, twilioClient); 21 | 22 | spinner.stop(); 23 | console.log(`\nRemoved assistant with UniqueName: ${args.assistant}`); 24 | 25 | console.log(`\nRemoved assistant with UniqueName: ${args.assistant}`) 26 | } catch (err) { 27 | 28 | spinner.stop(); 29 | console.error(`ERROR: ${err}`); 30 | } 31 | }; -------------------------------------------------------------------------------- /cmd/update.js: -------------------------------------------------------------------------------- 1 | const path = require('path'), 2 | ora = require('ora'), 3 | AutopilotCore = require('@dabblelab/autopilot-core'); 4 | 5 | module.exports = async (args) => { 6 | 7 | if (!args.hasOwnProperty('schema')) { 8 | console.log(`The '--schema' argument is required`); 9 | return; 10 | } 11 | 12 | const spinner = ora(); 13 | 14 | try { 15 | 16 | const schema = args.schema, 17 | profile = args.credentials || "default", 18 | assistantSid = args.assistant || false, 19 | twilioClient = await require('../lib/twilio-assistant/client')(profile); 20 | 21 | spinner.start('Updating assistant...'); 22 | const fullPath = `${path.resolve()}/${schema}`; 23 | 24 | const assistant = await AutopilotCore.updateAssistant(fullPath, twilioClient, assistantSid); 25 | 26 | spinner.stop(); 27 | 28 | console.log(`\nAssistant "${assistant.uniqueName}" was updated`); 29 | 30 | } catch (err) { 31 | 32 | spinner.stop(); 33 | console.log(err); 34 | console.error(`ERROR: ${err}`); 35 | } 36 | } -------------------------------------------------------------------------------- /test/alexa_model/model.json: -------------------------------------------------------------------------------- 1 | { 2 | "interactionModel": { 3 | "languageModel": { 4 | "invocationName": "basic starter", 5 | "types": [ 6 | 7 | ], 8 | "intents": [ 9 | { 10 | "name": "AMAZON.CancelIntent", 11 | "samples": [ 12 | 13 | ] 14 | }, 15 | { 16 | "name": "AMAZON.HelpIntent", 17 | "samples": [ 18 | 19 | ] 20 | }, 21 | { 22 | "name": "AMAZON.StopIntent", 23 | "samples": [ 24 | 25 | ] 26 | }, 27 | { 28 | "name":"MyNameIsIntent", 29 | "slots":[ 30 | { 31 | "name":"name", 32 | "type":"AMAZON.US_FIRST_NAME" 33 | } 34 | ], 35 | "samples":[ 36 | "{name}", 37 | "{name} is my name", 38 | "my name is {name}", 39 | "i am {name}", 40 | "you can call me {name}" 41 | ] 42 | } 43 | ] 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /cmd/simulate.js: -------------------------------------------------------------------------------- 1 | const ora = require('ora'), 2 | prettyJSONStringify = require('pretty-json-stringify'), 3 | AutopilotCore = require('@dabblelab/autopilot-core'); 4 | 5 | module.exports = async (args) => { 6 | 7 | if (!args.hasOwnProperty('assistant')) { 8 | console.log(`The '--assistant' argument is required`); 9 | return 10 | } 11 | if (!args.hasOwnProperty('text')) { 12 | console.log(`The '--text' argument is required`); 13 | return 14 | } 15 | 16 | const assistantSid = args.assistant, 17 | text = args.text, 18 | channel = 'cli', 19 | profile = args.credentials || "default", 20 | twilioClient = await require('../lib/twilio-assistant/client')(profile); 21 | 22 | const spinner = ora().start('Sending text to channel...'); 23 | 24 | try { 25 | 26 | const channelResponse = await AutopilotCore.customChannel(assistantSid, channel, text, twilioClient); 27 | 28 | spinner.stop(); 29 | console.log(`Channel response\n`); 30 | console.log(prettyJSONStringify(channelResponse)); 31 | 32 | } catch (err) { 33 | 34 | spinner.stop(); 35 | console.error(`ERROR: ${err}`); 36 | } 37 | } -------------------------------------------------------------------------------- /cmd/create.js: -------------------------------------------------------------------------------- 1 | const path = require('path'), 2 | ora = require('ora'), 3 | AutopilotCore = require('@dabblelab/autopilot-core'); 4 | 5 | module.exports = async (args) => { 6 | 7 | const spinner = ora(); 8 | 9 | try { 10 | 11 | let schema = args.schema || 'templates', 12 | profile = args.credentials || "default" 13 | 14 | let clonedAssistant = ''; 15 | 16 | if(schema == 'templates'){ 17 | 18 | let url = 'https://raw.githubusercontent.com/twilio/autopilot-templates/master/Assistants/templates.json'; 19 | 20 | clonedAssistant = await AutopilotCore.cloneTemplate(url); 21 | 22 | schema = path.join(clonedAssistant, 'schema.json'); 23 | 24 | } 25 | 26 | let fullPath = `${path.resolve()}/${schema}`, 27 | twilioClient = await require('../lib/twilio-assistant/client')(profile); 28 | 29 | spinner.start('Creating assistant...'); 30 | 31 | const assistant = await AutopilotCore.createAssistant(fullPath, twilioClient); 32 | 33 | spinner.stop(); 34 | console.log(`Assistant "${assistant.uniqueName}" was created`); 35 | 36 | } catch (err) { 37 | 38 | spinner.stop(); 39 | console.error(`ERROR: ${err}`); 40 | } 41 | } -------------------------------------------------------------------------------- /cmd/field-value.js: -------------------------------------------------------------------------------- 1 | const path = require('path'), 2 | ora = require('ora'), 3 | AutopilotCore = require('@dabblelab/autopilot-core'); 4 | 5 | module.exports = async (args) => { 6 | 7 | if (!args.hasOwnProperty('assistant')) { 8 | console.log(`The '--assistant' argument is required`); 9 | return; 10 | } 11 | if (!args.hasOwnProperty('field')) { 12 | console.log(`The '--field' argument is required`); 13 | return; 14 | } 15 | if (!args.hasOwnProperty('csv')) { 16 | console.log(`The '--csv' argument is required`); 17 | return; 18 | } 19 | 20 | const assistantSid = args.assistant, 21 | fieldUniqueName = args.field, 22 | csvPath = args.csv, 23 | profile = args.credentials || "default", 24 | twilioClient = await require('../lib/twilio-assistant/client')(profile); 25 | 26 | let fullPath = `${path.resolve()}/${csvPath}`; 27 | 28 | const spinner = ora().start('Adding field values...'); 29 | 30 | try { 31 | 32 | const assistant = await AutopilotCore.bulkUploadFieldValues(assistantSid, fieldUniqueName, csvPath, twilioClient); 33 | 34 | spinner.stop(); 35 | console.log(`Field values added to the assistant '${assistant.uniqueName}'`); 36 | } catch (err) { 37 | 38 | spinner.stop(); 39 | console.error(`ERROR: ${err}`); 40 | } 41 | } -------------------------------------------------------------------------------- /cmd/imports/alexa.js: -------------------------------------------------------------------------------- 1 | const path = require('path'), 2 | ora = require('ora'), 3 | AutopilotCore = require('@dabblelab/autopilot-core'); 4 | 5 | module.exports = async (args) => { 6 | 7 | if (!args.hasOwnProperty('model')) { 8 | console.log(`The '--model' argument is required`); 9 | return; 10 | } 11 | 12 | const model = args.model, 13 | redirectURL = args.redirectURL || 'https://inquisitive-stretch-2083.twil.io/generic', 14 | profile = args.credentials || "default", 15 | twilioClient = await require('../../lib/twilio-assistant/client')(profile); 16 | 17 | let fullPath = `${path.resolve()}/${model}`; 18 | 19 | const spinner = ora().start('Importing assistant...'); 20 | 21 | try { 22 | 23 | let assistant = {}; 24 | const filename = await AutopilotCore.importAlexaModel(fullPath, redirectURL); 25 | const alexaFulPath = path.resolve(process.cwd(),filename); 26 | 27 | if(await AutopilotCore.existAssistant(alexaFulPath, twilioClient)){ 28 | 29 | assistant = await AutopilotCore.updateAssistant(alexaFulPath, twilioClient); 30 | }else { 31 | 32 | assistant = await AutopilotCore.createAssistant(alexaFulPath, twilioClient); 33 | } 34 | 35 | spinner.stop(); 36 | console.log(`Assistant "${assistant.uniqueName}" was imported`); 37 | }catch (err) { 38 | 39 | spinner.stop(); 40 | console.error(`ERROR: ${err}`); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /check-version.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const package = require('./package.json') 4 | 5 | const current_version = `${process.version.substr(1,process.version.length)}`, 6 | expected_version = `${package.engines.node.substr(2,package.engines.node.length)}`; 7 | 8 | if(!compare(current_version,expected_version)){ 9 | console.error(`Node 8.10.0 or later is required.`); 10 | process.exit(1); 11 | } 12 | 13 | // comparing node version 14 | function compare(a, b) { 15 | if (a === b) { 16 | return true; 17 | } 18 | 19 | var a_components = a.split("."); 20 | var b_components = b.split("."); 21 | 22 | var len = Math.min(a_components.length, b_components.length); 23 | 24 | // loop while the components are equal 25 | for (var i = 0; i < len; i++) { 26 | // A bigger than B 27 | if (parseInt(a_components[i]) > parseInt(b_components[i])) { 28 | return true; 29 | } 30 | 31 | // B bigger than A 32 | if (parseInt(a_components[i]) < parseInt(b_components[i])) { 33 | return false; 34 | } 35 | } 36 | 37 | // If one's a prefix of the other, the longer one is greater. 38 | if (a_components.length >= b_components.length) { 39 | return true; 40 | } 41 | 42 | if (a_components.length < b_components.length) { 43 | return false; 44 | } 45 | 46 | // Otherwise they are the same. 47 | return true; 48 | } 49 | 50 | 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@twilio/autopilot-cli", 3 | "version": "0.0.24", 4 | "bin": { 5 | "ta": "./bin/cli.js" 6 | }, 7 | "description": "Twilio Autopilot Command Line Interface", 8 | "main": "index.js", 9 | "scripts": { 10 | "test": "mocha --timeout 0 --reporter spec", 11 | "preinstall": "node check-version.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/twilio/autopilot-cli.git" 16 | }, 17 | "keywords": [ 18 | "twilio, bots, conversational-ai, bot-development, virtual-assistant, bot-platform, bot-api, bot-cli, alexa-cli, google-assistant-cli, ivr-cli" 19 | ], 20 | "author": "Twilio", 21 | "preferGlobal": true, 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/twilio/autopilot-cli/issues" 25 | }, 26 | "homepage": "https://github.com/twilio/autopilot-cli#readme", 27 | "dependencies": { 28 | "@dabblelab/autopilot-core": "^1.0.0-beta.6", 29 | "chalk": "^2.4.2", 30 | "clear": "^0.1.0", 31 | "figlet": "^1.2.4", 32 | "inquirer": "^6.5.2", 33 | "lodash": "^4.17.15", 34 | "minimist": "^1.2.0", 35 | "ora": "^3.4.0", 36 | "pretty-json-stringify": "0.0.2", 37 | "twilio": "^3.35.0" 38 | }, 39 | "devDependencies": { 40 | "chai": "^4.2.0", 41 | "mocha": "^6.2.0" 42 | }, 43 | "engines": { 44 | "node": ">=8.10.0" 45 | }, 46 | "directories": { 47 | "lib": "lib", 48 | "test": "test" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cmd/imports/dialogFlow.js: -------------------------------------------------------------------------------- 1 | const path = require('path'), 2 | ora = require('ora'), 3 | AutopilotCore = require('@dabblelab/autopilot-core'); 4 | 5 | module.exports = async (args) => { 6 | 7 | if (!args.hasOwnProperty('dfbackup')) { 8 | console.log(`The '--dfbackup' argument is required`); 9 | return; 10 | } 11 | if (!args.hasOwnProperty('dfagent')) { 12 | console.log(`The '--dfagent' argument is required`); 13 | return; 14 | } 15 | 16 | const dfbackup = args.dfbackup, 17 | name = args.dfagent, 18 | profile = args.credentials || "default", 19 | twilioClient = await require('../../lib/twilio-assistant/client')(profile); 20 | 21 | let fullPath = `${path.resolve()}/${dfbackup}`; 22 | 23 | const spinner = ora().start('Importing assistant...'); 24 | 25 | try { 26 | 27 | const filename = await AutopilotCore.importDialogFlowAgent(fullPath, name); 28 | 29 | const dfbackupFulPath = path.resolve(process.cwd(),filename); 30 | 31 | let assistant = {}; 32 | 33 | if(await AutopilotCore.existAssistant(dfbackupFulPath, twilioClient)){ 34 | 35 | assistant = await AutopilotCore.updateAssistant(dfbackupFulPath, twilioClient); 36 | }else { 37 | 38 | assistant = await AutopilotCore.createAssistant(dfbackupFulPath, twilioClient); 39 | } 40 | 41 | spinner.stop(); 42 | console.log(`Assistant "${assistant.uniqueName}" was imported`); 43 | } catch (err) { 44 | 45 | spinner.stop(); 46 | console.error(`ERROR: ${err}`); 47 | } 48 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const minimist = require('minimist'); 2 | const error = require('./lib/error'); 3 | 4 | module.exports = () => { 5 | //TODO: check to see if CLI is configured 6 | 7 | const args = minimist(process.argv.slice(2)); 8 | 9 | let cmd = args._[0] || 'help'; 10 | 11 | if (args.version || args.v) { 12 | cmd = 'version' 13 | } 14 | 15 | if (args.help || args.h) { 16 | cmd = 'help' 17 | } 18 | 19 | switch (cmd) { 20 | case 'temp': 21 | require('./cmd/temp')(args); 22 | break; 23 | 24 | case 'init': 25 | require('./cmd/init')(args); 26 | break; 27 | 28 | case 'list': 29 | require('./cmd/list')(args); 30 | break; 31 | 32 | case 'create': 33 | require('./cmd/create')(args); 34 | break; 35 | 36 | case 'update': 37 | require('./cmd/update')(args); 38 | break; 39 | 40 | case 'delete': 41 | require('./cmd/delete')(args); 42 | break; 43 | 44 | case 'export': 45 | require('./cmd/export')(args); 46 | break; 47 | 48 | case 'import': 49 | require('./cmd/imports')(args); 50 | break; 51 | 52 | case 'simulate': 53 | require('./cmd/simulate')(args); 54 | break; 55 | 56 | case 'field': 57 | require('./cmd/field-value')(args); 58 | break; 59 | 60 | case 'version': 61 | require('./cmd/version')(args); 62 | break; 63 | 64 | case 'help': 65 | require('./cmd/help')(args); 66 | break; 67 | 68 | default: 69 | error(`"${cmd}" is not a valid command!`, true); 70 | break 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /cmd/export.js: -------------------------------------------------------------------------------- 1 | const AutopilotCore = require('@dabblelab/autopilot-core'), 2 | inquirer = require('inquirer'), 3 | ora = require('ora'); 4 | 5 | module.exports = async (args) => { 6 | 7 | const spinner = await ora(); 8 | let seletedAssistant = ''; 9 | 10 | try { 11 | 12 | const profile = args.credentials || "default", 13 | assistantSid = args.assistant || '', 14 | twilioClient = await require('../lib/twilio-assistant/client')(profile); 15 | 16 | if(assistantSid){ 17 | 18 | seletedAssistant = assistantSid; 19 | }else{ 20 | 21 | spinner.start(`Getting assistant List...\n`); 22 | 23 | const fullData = await AutopilotCore.listAssistant(twilioClient); 24 | spinner.stop(); 25 | 26 | if(fullData.length){ 27 | 28 | const choices = await fullData.map(x => {return x.uniqueName}); 29 | 30 | const answer = await inquirer.prompt([ 31 | { 32 | type: 'list', 33 | name: 'assistantName', 34 | message: 'Choose your assistant: ', 35 | choices: choices 36 | } 37 | ]); 38 | 39 | seletedAssistant = answer.assistantName; 40 | }else{ 41 | 42 | console.log('no assistants.'); 43 | return; 44 | } 45 | } 46 | 47 | spinner.start(`Exporting assistant...`); 48 | 49 | const assistant = await AutopilotCore.exportAssistant(seletedAssistant, twilioClient); 50 | 51 | spinner.stop(); 52 | console.log(`\nAssistant exported in ${assistant.filename}`); 53 | } catch (err) { 54 | 55 | spinner.stop(); 56 | console.error(`ERROR: ${err}`); 57 | } 58 | } -------------------------------------------------------------------------------- /lib/twilio-assistant/client.js: -------------------------------------------------------------------------------- 1 | 2 | const twilio = require('twilio'), 3 | os = require('os'), 4 | path = require('path') 5 | _ = require('lodash'), 6 | inquirer = require('inquirer'), 7 | {_askCredential} = require('./initConfig'); 8 | 9 | module.exports = async (profileName) => { 10 | try { 11 | const twilioDir = path.resolve(os.homedir(),'.twilio/config.json'); 12 | let config = require(twilioDir); 13 | let f_profile = await _.findIndex(config, { profileName: profileName }); 14 | 15 | if (f_profile >= 0) { 16 | return await new twilio(config[f_profile].accountSID, config[f_profile].authToken); 17 | } else { 18 | return await inquirer.prompt([{ 19 | type: 'confirm', 20 | name: 'newcreate', 21 | default: true, 22 | message: 'The Credentials name [' + profileName + '] does not exist. Would you like to create it?' 23 | }]).then(async (answer) => { 24 | if (answer.newcreate) { 25 | let config = await _askCredential(profileName); 26 | let f_profile = await _.findIndex(config, { profileName: profileName }); 27 | if (f_profile >= 0) { 28 | return await new twilio(config[f_profile].accountSID, config[f_profile].authToken); 29 | } 30 | } else { 31 | process.exit(); 32 | } 33 | }); 34 | } 35 | } catch(e) { 36 | if (e.code == 'MODULE_NOT_FOUND') { 37 | console.log(`Oops! We did not find the "${profileName}" Credentials name in the System Home Directory.`); 38 | 39 | const config = await _askCredential('default'); 40 | const f_profile = _.findIndex(config, { profileName: 'default' }); 41 | if (f_profile >= 0) { 42 | return new twilio(config[f_profile].accountSID, config[f_profile].authToken); 43 | } 44 | } else { 45 | console.log(`Oops! Invalid credentials for the "${profileName}" Credentials name.`); 46 | process.exit(); 47 | } 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /lib/json_utility.js: -------------------------------------------------------------------------------- 1 | 2 | const os = require('os'), 3 | path = require('path'), 4 | fs = require('fs'), 5 | prettyJSONStringify = require('pretty-json-stringify') 6 | _ = require('lodash'); 7 | 8 | const twilioDir = path.resolve(os.homedir(),'.twilio'); 9 | const filePath = path.resolve(os.homedir(),'.twilio','config.json'); 10 | 11 | const addProfile = async (profileName,valid_credential_object) => { 12 | const n_profile = { 13 | profileName : profileName, 14 | accountSID : valid_credential_object.account_sid, 15 | authToken : valid_credential_object.auth_token 16 | }; 17 | 18 | if(fs.existsSync(filePath)){ 19 | const profiles = await read(filePath); 20 | if(profiles.length){ 21 | const p_index = _.findIndex(profiles,{profileName : profileName}); 22 | if(p_index>=0){ 23 | profiles[p_index] = n_profile; 24 | }else{ 25 | profiles.push(n_profile); 26 | } 27 | }else{ 28 | profiles.push(n_profile); 29 | } 30 | return write(profiles); 31 | } 32 | else{ 33 | let profiles = []; 34 | profiles.push(n_profile); 35 | return write(profiles); 36 | } 37 | } 38 | 39 | const read = ()=>{ 40 | try { 41 | let content = require(filePath); 42 | return content; 43 | } catch (e) { 44 | return []; 45 | } 46 | } 47 | 48 | const write = async (jsonObject) =>{ 49 | if(directoryExists(twilioDir)){ 50 | try{ 51 | await fs.writeFileSync(filePath, prettyJSONStringify(jsonObject)); 52 | return await read(); 53 | } 54 | catch(err){ 55 | console.error('Invalid file, cannot write to: ' + filePath); 56 | process.exit(); 57 | } 58 | }else{ 59 | console.error('Invalid directory : ' + twilioDir); 60 | process.exit(); 61 | } 62 | } 63 | 64 | function directoryExists(twilioDir){ 65 | if(fs.existsSync(twilioDir)){ 66 | return true; 67 | }else{ 68 | try{ 69 | fs.mkdirSync(twilioDir); 70 | return true 71 | }catch(e){ 72 | return false; 73 | } 74 | } 75 | } 76 | module.exports = { 77 | addProfile, 78 | read 79 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | > :warning: **The Autopilot CLI is now deprecated**: Please use the Autopilot Plugin for the Twilio CLI found here https://www.twilio.com/docs/autopilot/twilio-autopilot-cli 2 | 3 | Twilio Autopilot CLI 4 | === 5 | A command line interface for managing your Twilio Autopilot bots. After installing you'll be able to: 6 | 7 | * Create an assistant from a template 8 | * Export an existing assistant to a json file 9 | * Update an existing assistant with a json file 10 | * Delete an assistant 11 | * Simulate an Assistant 12 | * Import a DialogFlow Agent/Alexa Interaction Model 13 | * Bulk upload field values 14 | 15 | The Autopilot CLI enable you to: 16 | 17 | * Keep your assistant in a repository with version control 18 | * Integrate with your CI environment 19 | * Share the schema files to collaborate on development 20 | 21 | ## Installation 22 | 23 | `sudo npm install -g @twilio/autopilot-cli` 24 | 25 | ## Usage 26 | 27 | ``` 28 | Usage: 29 | ta init [--twilio-setup | --list | --credentials ] 30 | ta list [--credentials ] 31 | ta create [--schema ] [--credentials ] 32 | ta update --schema [--assistant ] [--credentials ] 33 | ta delete --assistant [--credentials ] 34 | ta export --assistant [--credentials ] 35 | ta import --dfbackup --dfagent [--credentials ] 36 | OR 37 | ta import dialogflow --dfbackup --dfagent [--credentials ] 38 | ta import alexa --model [--redirectURL ] [--credentials ] 39 | ta simulate --assistant --text [--credentials ] 40 | ta field --assistant --field --csv [--credentials ] 41 | 42 | Options: 43 | -h --help Help Screen 44 | -v --version CLI Version 45 | --twilio-setup To setup new credential in autopilot-cli 46 | --list Shows list of configured credentials in autopilot-cli 47 | --schema Autopilot Schema File/DialogFlow Schema Zip File 48 | --assistant Twilio Autopilot Assistant SID 49 | --dfagent Dialogflow Agent Name 50 | --dfbackup Dialogflow Agent Backup Zip File 51 | --text The user text input 52 | --field Twilio Autopilot Field Type SID 53 | --csv CSV File path 54 | --model Alexa Interaction Model File 55 | --redirectURL Alexa Back-End Hanlder URL to send back the response 56 | ``` 57 | 58 | ## Configuration 59 | To use the Twilio Autopilot CLI, you first need to configure your Twilio Account SID and Auth Token. Optionally, multiple accounts can be configured using the `--credentials` option to associate an Account SID and Auth Token with a profile name. 60 | 61 | The first time you use Autopilot CLI, you must call the `$ ta init` or `$ ta init --twilio-setup` command to initialize the tool with your Twilio account. 62 | 63 | To update an existing autopilot-cli credentail use following command: 64 | `$ ta init --credentials `. 65 | 66 | ## Schema Files 67 | A schema file is a JSON document that's used to define an Autopilot assistant. They tell the CLI what to create or update. When exporting an Assistant will one be saved. When creating an Assistant you can choose from one of the following [templates](https://github.com/twilio/autopilot-templates/tree/master/Assistants) to get started quickly. 68 | -------------------------------------------------------------------------------- /lib/files.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'), 2 | path = require('path'), 3 | prettyJSONStringify = require('pretty-json-stringify'), 4 | os = require('os'), 5 | _ = require('lodash'); 6 | 7 | const twilioRecoverySchemaDir = path.resolve(os.homedir(),'.twilio/recovery_schema'), 8 | filePath = path.resolve(os.homedir(),'.twilio/recovery_schema','restoreSchema.json'); 9 | 10 | module.exports = { 11 | getCurrentDirectoryBase : () => { 12 | return path.basename(process.cwd()); 13 | }, 14 | 15 | directoryExists : (filePath) => { 16 | try { 17 | return fs.statSync(filePath).isDirectory(); 18 | } catch (err) { 19 | return false; 20 | } 21 | }, 22 | 23 | fileExists : (filePath) => { 24 | try { 25 | return fs.statSync(filePath).isFile(); 26 | } catch (err) { 27 | return false; 28 | } 29 | }, 30 | 31 | createAssistantJSONFile : async (filename, recoverSchema = false) => { 32 | if(recoverSchema){ 33 | if(checkDirectoryExists()){ 34 | await fs.writeFileSync(path.join(twilioRecoverySchemaDir,`backup-${filename}.json`),prettyJSONStringify({})); 35 | return `backup-${filename}.json`; 36 | } 37 | }else{ 38 | try{ 39 | if(fs.existsSync(path.join(process.cwd(),`${filename}.json`))){ 40 | filename = `${filename}-${Date.now()}.json`; 41 | await fs.writeFileSync(path.join(process.cwd(),filename)); 42 | return filename; 43 | }else{ 44 | await fs.writeFileSync(path.join(process.cwd(),`${filename}.json`),{}); 45 | return `${filename}.json`; 46 | } 47 | } catch (err){ 48 | return err; 49 | } 50 | } 51 | }, 52 | 53 | writeAssistantJSONFile : async(data, filename, recoverSchema = false) => { 54 | if(recoverSchema){ 55 | 56 | const writeData = await fs.writeFileSync(path.join(twilioRecoverySchemaDir,filename),prettyJSONStringify(data)); 57 | 58 | let restoreSchemaJSON = await readRecoverSchema(); 59 | 60 | if(restoreSchemaJSON.length){ 61 | const f_index = _.findIndex(restoreSchemaJSON,{name : data.uniqueName}); 62 | 63 | if(f_index>=0){ 64 | restoreSchemaJSON[f_index] = { 65 | "name" : data.uniqueName, 66 | "schema_path" : filename 67 | } 68 | }else{ 69 | restoreSchemaJSON.push({ 70 | "name" : data.uniqueName, 71 | "schema_path" : filename 72 | }) 73 | } 74 | }else{ 75 | restoreSchemaJSON.push({ 76 | "name" : data.uniqueName, 77 | "schema_path" : filename 78 | }) 79 | } 80 | 81 | const updateRestoreSchema = await await fs.writeFileSync(filePath,prettyJSONStringify(restoreSchemaJSON)); 82 | 83 | return writeData; 84 | }else{ 85 | return fs.writeFileSync(path.join(process.cwd(),filename),prettyJSONStringify(data)); 86 | } 87 | }, 88 | 89 | removeFile : async(filePath) => { 90 | try{ 91 | return fs.unlinkSync(filePath); 92 | }catch(e) { 93 | console.log(e.message); 94 | return e; 95 | } 96 | } 97 | 98 | }; 99 | 100 | function checkDirectoryExists(){ 101 | if(fs.existsSync(twilioRecoverySchemaDir)){ 102 | return true; 103 | }else{ 104 | try{ 105 | fs.mkdirSync(twilioRecoverySchemaDir); 106 | return true 107 | }catch(e){ 108 | return false; 109 | } 110 | } 111 | } 112 | 113 | function readRecoverSchema(){ 114 | try { 115 | let content = require(filePath); 116 | return content; 117 | } catch (e) { 118 | return []; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /cmd/help.js: -------------------------------------------------------------------------------- 1 | const clear = require('clear'); 2 | const chalk = require('chalk'); 3 | const figlet = require('figlet'); 4 | 5 | //TODO: finish the help screen 6 | const menus = { 7 | main: `Twilio Autopilot CLI 8 | Usage: 9 | ta [command] 10 | init ............... configure auth setting 11 | list ............... list existing assistants 12 | create ............. create a new assistant 13 | update ............. update an assistant 14 | delete ............. delete an assistant 15 | export ............. export assistant schema 16 | import ............. import Dialogflow Agent Backup Zip/Alexa Interaction Model File 17 | simulate ........... sending a message to the custom channel endpoint 18 | field .............. bulk uploading field values 19 | version ............ get package version 20 | help ............... get help menu for a command`, 21 | 22 | init: ` Usage: 23 | ta init 24 | Options: 25 | --credentials ...... [optional] name for the credentials to be created/updated 26 | --list.............. [optional] list all the profile(s) for autopilot-cli 27 | --twilio-setup...... [optional] setup Twilio credentials with "accountSID" and "authToken"`, 28 | 29 | list: ` Usage: 30 | ta list 31 | Options: 32 | --credentials ...... [optional] credentials name`, 33 | 34 | create: ` Usage: 35 | ta create 36 | Options: 37 | --schema ........... [option] Autopilot Schema File, 38 | --credentials ...... [optional] credentials name`, 39 | 40 | update: ` Usage: 41 | ta update 42 | Options: 43 | --schema ........... Autopilot Schema File 44 | --assistant ........ [optional] Twilio Autopilot Assistant SID 45 | --credentials ...... [optional] credentials name`, 46 | 47 | delete: ` Usage: 48 | ta delete 49 | Options: 50 | --assistant ........ Twilio Autopilot Assistant SID 51 | --credentials ...... [optional] credentials name`, 52 | 53 | export: ` Usage: 54 | ta export 55 | Options: 56 | --assistant ........ [optional] Twilio Autopilot Assistant SID 57 | --credentials ...... [optional] credentials name`, 58 | 59 | import: ` Usage: 60 | ta import [command] 61 | Options: 62 | --help ................ output usage information 63 | Commands: 64 | alexa ........ import alexa interaction model, 65 | dialogFlow ... import dialogFlow agent backup zip`, 66 | 67 | dialogflow : ` Usage: 68 | ta import dialogflow 69 | Options : 70 | --dfbackup ............. Dialogflow Agent Backup Zip File, 71 | --dfagent .............. Dialogflow Agent Name, 72 | --credentials ...... [optional] credentials name`, 73 | 74 | alexa : ` Usage: 75 | ta import alexa 76 | Options : 77 | --model ............. Alexa Interaction Model File, 78 | --redirectURL ....... [optional] Back-End Handler URL 79 | --credentials, -c ...... [optional] credentials name`, 80 | 81 | channel: ` Usage: 82 | ta simulate 83 | Options: 84 | --assistant ............ Twilio Autopilot Assistant SID, 85 | --text ................. User text input, 86 | --credentials ...... [optional] credentials name`, 87 | 88 | field: ` Usage: 89 | ta field 90 | Options: 91 | --assistant ............ Twilio Autopilot Assistant SID, 92 | --field ................ field type SID, 93 | --csv .................. CSV file, 94 | --credentials ...... [optional] credentials name`, 95 | 96 | version: ` Usage: 97 | ta version `, 98 | } 99 | 100 | module.exports = (args) => { 101 | 102 | clear() 103 | 104 | const subCmd = args._[0] === 'help' 105 | ? args._[1] 106 | : args._.length === 2 ? args._[1] : args._[0] 107 | 108 | console.log( 109 | chalk.red( 110 | figlet.textSync('Twilio Autopilot', { horizontalLayout: 'full' }) 111 | ) 112 | ) 113 | 114 | console.log(menus[subCmd] || menus.main) 115 | } -------------------------------------------------------------------------------- /test/ta_test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const expect = require('chai').expect; 3 | const path = require('path'); 4 | const files = require('../lib/files'); 5 | 6 | const AutopilotCore = require('@dabblelab/autopilot-core'); 7 | 8 | describe('Twilio Autopilot CLI Module Tests', () => { 9 | let assistant = {}, 10 | profile = 'default', 11 | df_filename = '', 12 | alexa_filename = '', 13 | twilioClient = ''; 14 | 15 | 16 | 17 | before(async () => { 18 | 19 | twilioClient = await require('../lib/twilio-assistant/client')(profile); 20 | }) 21 | after(() => { 22 | files.removeFile(path.join(process.cwd(), `${assistant.uniqueName}.json`)); 23 | files.removeFile(path.join(process.cwd(), df_filename)); 24 | files.removeFile(path.join(process.cwd(), alexa_filename)); 25 | }) 26 | describe('#createAssistant()', () => { 27 | it('create assistant', async () => { 28 | 29 | 30 | let fullPath = `${path.resolve('test/test_assistant.json')}`; 31 | assistant = await AutopilotCore.createAssistant(fullPath, twilioClient); 32 | expect(assistant).to.have.property('uniqueName'); 33 | }); 34 | }); 35 | 36 | describe('#getAssistants()', () => { 37 | it('list assistants', async () => { 38 | 39 | const assistants = await AutopilotCore.listAssistant(twilioClient); 40 | expect(assistants.length).to.be.greaterThan(0, 'no assistants found'); 41 | }); 42 | }); 43 | 44 | describe('#exportAssistant()', () => { 45 | it('export assistant', async () => { 46 | 47 | const export_assistant = await AutopilotCore.exportAssistant(assistant.uniqueName, twilioClient); 48 | expect(export_assistant).to.have.property('uniqueName'); 49 | }); 50 | }); 51 | 52 | describe('#updateAssistant()', () => { 53 | it('update assistant', async () => { 54 | 55 | const schemaPath = path.join(process.cwd(),`${assistant.uniqueName}.json`); 56 | const update_assistant = await AutopilotCore.updateAssistant(schemaPath, twilioClient); 57 | expect(update_assistant).to.have.property('uniqueName'); 58 | }); 59 | }); 60 | 61 | describe('#customChannel()', () => { 62 | it('custom channel message', async () => { 63 | 64 | const channelResponse = await AutopilotCore.customChannel(assistant.sid, 'webchat', 'hello', twilioClient); 65 | expect(channelResponse).to.have.property('says'); 66 | }); 67 | }); 68 | 69 | describe('#bulkUploadFieldValues()', () => { 70 | it('bulk upload field values', async () => { 71 | 72 | const csvFile = `${path.resolve('test/Yes.csv')}`; 73 | const bulkUploadFieldValues = await AutopilotCore.bulkUploadFieldValues(assistant.uniqueName, 'Yes', csvFile, twilioClient); 74 | expect(bulkUploadFieldValues).to.have.property('uniqueName'); 75 | }); 76 | }); 77 | 78 | describe('#deleteAssistant()', () => { 79 | it('delete assistant', async () => { 80 | 81 | const delete_assistant = await AutopilotCore.deleteAssistant(assistant.uniqueName, twilioClient); 82 | expect(delete_assistant).to.be.true; 83 | }); 84 | }); 85 | 86 | describe('#importAssistant()', () => { 87 | it("import DialogFlow Assistant", async () => { 88 | 89 | let fullPath = `${path.resolve('test/Twilio-Basic-Starter.zip')}`, 90 | name = `Twilio-Basic-Starter`; 91 | 92 | const filename = await AutopilotCore.importDialogFlowAgent(fullPath, name); 93 | df_filename = filename; 94 | expect(path.extname(filename)).to.be.eq('.json'); 95 | }) 96 | }) 97 | 98 | describe('#importAlexaAssistant()', () => { 99 | it("import Alexa Interaction Model Assistant", async () => { 100 | 101 | let fullPath = `${path.resolve('test/alexa_model/model.json')}`, 102 | redirectURL = `https://inquisitive-stretch-2083.twil.io/generic`; 103 | 104 | const filename = await AutopilotCore.importAlexaModel(fullPath, redirectURL); 105 | alexa_filename = filename; 106 | expect(path.extname(filename)).to.be.eq('.json'); 107 | }) 108 | }) 109 | 110 | 111 | }); -------------------------------------------------------------------------------- /lib/twilio-assistant/initConfig.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer'), 2 | json_utility = require('../json_utility'); 3 | 4 | const initConfig = function (args) { 5 | const option = args.hasOwnProperty('credentials') ? 'profile' : args.hasOwnProperty('list') ? 'list' : 'setup'; 6 | 7 | switch (option) { 8 | case 'setup': 9 | twilioInitSetUp(); 10 | break; 11 | 12 | case 'profile': 13 | twilioInitProfile(args.credentials); 14 | break; 15 | 16 | case 'list': 17 | twilioInitProfileList(); 18 | break; 19 | } 20 | }; 21 | 22 | // create new profile 23 | const twilioInitSetUp = () => { 24 | if (json_utility.read().length) { 25 | inquirer.prompt([{ 26 | message: 'Please type in your new credentials name:\n', 27 | type: 'input', 28 | name: 'profile', 29 | default: 'default' 30 | }]).then((answer) => { 31 | let newProfileName = answer.profile.trim(); 32 | 33 | if (!/^[a-zA-Z0-9-_]+$/i.test(newProfileName)) { 34 | console.error(`[Error]: Invalid credentials name. The credentials name '${profileName.trim()}' contains characters that are not allowed.`); 35 | } 36 | _askCredential(newProfileName); 37 | }); 38 | } 39 | else { 40 | _askCredential('default'); 41 | } 42 | }; 43 | 44 | // update existing profile 45 | const twilioInitProfile = (profileName) => { 46 | 47 | if (!/^[a-zA-Z0-9-_]+$/i.test(profileName.trim())) { 48 | console.error(`[Error]: Invalid credentials name. The credentials name '${profileName.trim()}' contains characters that are not allowed.`); 49 | } else { 50 | const p_index = _.findIndex(json_utility.read(), { profileName: profileName.trim() }); 51 | if (p_index >= 0) { 52 | _confirmOverwritingProfile(profileName, (confirm) => { 53 | if (confirm) { 54 | _askCredential(profileName.trim()); 55 | } 56 | }) 57 | } 58 | else { 59 | // console.error('[Error]: The credentials name [' + profileName + '] does not exists.'); 60 | // process.exit(1); 61 | _askCredential(profileName.trim()); 62 | } 63 | } 64 | 65 | }; 66 | 67 | // list all associated profiles 68 | const twilioInitProfileList = () => { 69 | console.log(`\n credentials`.padEnd(30)+` | Associated Twilio credentials`); 70 | console.log('------------------------------------------------------------------------'); 71 | json_utility.read().forEach(element => { 72 | console.log(` [${element.profileName}]`.padEnd(30)+`| "${element.profileName}"`); 73 | }); 74 | }; 75 | 76 | // get account_sid and auth_token 77 | const _askCredential = async (profileName) => { 78 | console.log('\nPlease visit https://www.twilio.com/console' + 79 | '\nFill in the Twilio ACCOUNT SID and Twilio AUTH TOKEN below to save/modify your Twilio credentials.'); 80 | 81 | return await inquirer.prompt([ 82 | { 83 | type: 'input', 84 | name: 'accountSID', 85 | message: 'Twilio ACCOUNT SID:\n', 86 | validate: function (input) { 87 | if (!input.trim()) { 88 | return '"Twilio ACCOUNT SID" cannot be empty.'; 89 | } 90 | return true; 91 | } 92 | }, 93 | { 94 | type: 'input', 95 | name: 'authToken', 96 | message: 'Twilio AUTH TOKEN:\n', 97 | validate: function (input) { 98 | if (!input.trim()) { 99 | return '"Twilio AUTH TOKEN" cannot be empty.'; 100 | } 101 | return true; 102 | } 103 | } 104 | ]).then(async (answer) => { 105 | const valid_credential_object = { 106 | "account_sid": answer.accountSID.trim(), 107 | "auth_token": answer.authToken.trim() 108 | }; 109 | return await json_utility.addProfile(profileName, valid_credential_object); 110 | }); 111 | }; 112 | 113 | // confirming profile overwriting 114 | function _confirmOverwritingProfile(profileName, callback) { 115 | inquirer.prompt([ 116 | { 117 | type: 'confirm', 118 | name: 'overwrite', 119 | default: true, 120 | message: 'The credentials name [' + profileName + '] exists, do you want to overwrite it?' 121 | } 122 | ]).then((answer) => { 123 | callback(answer.overwrite); 124 | }); 125 | } 126 | 127 | module.exports = { initConfig, _askCredential }; 128 | -------------------------------------------------------------------------------- /test/test_assistant.json: -------------------------------------------------------------------------------- 1 | { 2 | "friendlyName" : "unit-test-assistant", 3 | "logQueries" : true, 4 | "uniqueName" : "unit-test-assistant", 5 | "defaults" : { 6 | "defaults" : { 7 | "assistant_initiation" : "task://welcome-intent", 8 | "fallback" : "task://fallback-Intent", 9 | "collect" : { 10 | "validate_on_failure" : "task://fallback-Intent" 11 | } 12 | } 13 | }, 14 | "styleSheet" : { 15 | "style_sheet" : { 16 | "voice" : { 17 | "say_voice" : "Polly.Salli" 18 | } 19 | } 20 | }, 21 | "fieldTypes" : [ 22 | { 23 | "uniqueName" : "Yes", 24 | "values" : [ 25 | { 26 | "language" : "en-US", 27 | "value" : "ok", 28 | "synonymOf" : "Yes" 29 | }, 30 | { 31 | "language" : "en-US", 32 | "value" : "yeah", 33 | "synonymOf" : "Yes" 34 | }, 35 | { 36 | "language" : "en-US", 37 | "value" : "yes please", 38 | "synonymOf" : "Yes" 39 | }, 40 | { 41 | "language" : "en-US", 42 | "value" : "okay", 43 | "synonymOf" : "Yes" 44 | }, 45 | { 46 | "language" : "en-US", 47 | "value" : "sounds correct", 48 | "synonymOf" : "Yes" 49 | }, 50 | { 51 | "language" : "en-US", 52 | "value" : "I think so", 53 | "synonymOf" : "Yes" 54 | }, 55 | { 56 | "language" : "en-US", 57 | "value" : "yes I agree", 58 | "synonymOf" : "Yes" 59 | }, 60 | { 61 | "language" : "en-US", 62 | "value" : "yes I do", 63 | "synonymOf" : "Yes" 64 | }, 65 | { 66 | "language" : "en-US", 67 | "value" : "I agree", 68 | "synonymOf" : "Yes" 69 | }, 70 | { 71 | "language" : "en-US", 72 | "value" : "Yes", 73 | "synonymOf" : null 74 | } 75 | ] 76 | }, 77 | { 78 | 79 | "uniqueName" : "No", 80 | "values" : [ 81 | { 82 | "language" : "en-US", 83 | "value" : "no thanks", 84 | "synonymOf" : "No" 85 | }, 86 | { 87 | "language" : "en-US", 88 | "value" : "Nothing for now", 89 | "synonymOf" : "No" 90 | }, 91 | { 92 | "language" : "en-US", 93 | "value" : "Nope", 94 | "synonymOf" : "No" 95 | }, 96 | { 97 | "language" : "en-US", 98 | "value" : "nothing", 99 | "synonymOf" : "No" 100 | }, 101 | { 102 | "language" : "en-US", 103 | "value" : "no maybe next time", 104 | "synonymOf" : "No" 105 | }, 106 | { 107 | "language" : "en-US", 108 | "value" : "nah", 109 | "synonymOf" : "No" 110 | }, 111 | { 112 | "language" : "en-US", 113 | "value" : "no not really", 114 | "synonymOf" : "No" 115 | }, 116 | { 117 | "language" : "en-US", 118 | "value" : "na", 119 | "synonymOf" : "No" 120 | }, 121 | { 122 | "language" : "en-US", 123 | "value" : "No", 124 | "synonymOf" : null 125 | } 126 | ] 127 | } 128 | ], 129 | "tasks" : [ 130 | { 131 | "uniqueName" : "cancel-appointment", 132 | "actions" : { 133 | "actions" : [ 134 | { 135 | "say" : { 136 | "speech" : "Okay, no problem! We will cancel your appointment. Have a great day." 137 | } 138 | }, 139 | { "listen" : true } 140 | ] 141 | }, 142 | "fields" : [], 143 | "samples" : [ 144 | { 145 | "language" : "en-US", 146 | "taggedText" : "I would like to cancel my appointment" 147 | }, 148 | { 149 | "language" : "en-US", 150 | "taggedText" : "I would like to cancel my book" 151 | }, 152 | { 153 | "language" : "en-US", 154 | "taggedText" : "cancel book" 155 | }, 156 | { 157 | "language" : "en-US", 158 | "taggedText" : "I would like to cancel my visit" 159 | }, 160 | { 161 | "language" : "en-US", 162 | "taggedText" : "cancel my visit" 163 | }, 164 | { 165 | "language" : "en-US", 166 | "taggedText" : "cancel my appointment" 167 | }, 168 | { 169 | "language" : "en-US", 170 | "taggedText" : "cancel an book" 171 | } 172 | ] 173 | }, 174 | { 175 | "uniqueName" : "confirm-appointment", 176 | "actions" : { 177 | "actions" : [ 178 | { 179 | "say" : { 180 | "speech" : "Great! Thank you for confirming your appointment. See you soon and have a great day." 181 | } 182 | }, 183 | { "listen" : true } 184 | ] 185 | }, 186 | "fields" : [], 187 | "samples" : [ 188 | { 189 | "language" : "en-US", 190 | "taggedText" : "I would like to confirm my appointment" 191 | }, 192 | { 193 | "language" : "en-US", 194 | "taggedText" : "I would like to confirm my booking" 195 | }, 196 | { 197 | "language" : "en-US", 198 | "taggedText" : "confirm my appointment" 199 | }, 200 | { 201 | "language" : "en-US", 202 | "taggedText" : "confirm my book" 203 | } 204 | ] 205 | }, 206 | { 207 | "uniqueName" : "fallback-Intent", 208 | "actions" : { 209 | "actions" : [ 210 | { 211 | "say" : { 212 | "speech" : "I didn't get that. Can you say it again?" 213 | } 214 | }, 215 | { "listen" : true } 216 | ] 217 | }, 218 | "fields" : [], 219 | "samples" : [] 220 | }, 221 | { 222 | "uniqueName" : "make-appointment", 223 | "actions" : { 224 | "actions" : [ 225 | { 226 | "say" : { 227 | "speech" : "Great! What day and time would be good for you?" 228 | } 229 | }, 230 | { "listen" : true } 231 | ] 232 | }, 233 | "fields" : [], 234 | "samples" : [ 235 | { 236 | "language" : "en-US", 237 | "taggedText" : "I would like to schedule an appointment" 238 | }, 239 | { 240 | "language" : "en-US", 241 | "taggedText" : "I would like to book an appointment" 242 | }, 243 | { 244 | "language" : "en-US", 245 | "taggedText" : "I would like to make an appointment" 246 | }, 247 | { 248 | "language" : "en-US", 249 | "taggedText" : "book appointment" 250 | }, 251 | { 252 | "language" : "en-US", 253 | "taggedText" : "schedule appointment" 254 | }, 255 | { 256 | "language" : "en-US", 257 | "taggedText" : "make an appointment" 258 | } 259 | ] 260 | }, 261 | { 262 | "uniqueName" : "manage-appointment", 263 | "actions" : { 264 | "actions" : [ 265 | { 266 | "say" : { 267 | "speech" : "Great! We will schedule your appointment. Would you a sms for your appointment detail?" 268 | } 269 | }, 270 | { "listen" : true } 271 | ] 272 | }, 273 | "fields" : [ 274 | { 275 | "uniqueName" : "time", 276 | "fieldType" : "Twilio.TIME" 277 | }, 278 | { 279 | "uniqueName" : "date", 280 | "fieldType" : "Twilio.DATE" 281 | } 282 | ], 283 | "samples" : [ 284 | { 285 | "language" : "en-US", 286 | "taggedText" : "{date} at {time}" 287 | }, 288 | { 289 | "language" : "en-US", 290 | "taggedText" : "{time} on {date}" 291 | } 292 | ] 293 | }, 294 | { 295 | "uniqueName" : "modify-appointment", 296 | "actions" : { 297 | "actions" : [ 298 | { 299 | "say" : { 300 | "speech" : "Great! What day and time would be good for you to re-schedule your appointment?" 301 | } 302 | }, 303 | { "listen" : true } 304 | ] 305 | }, 306 | "fields" : [], 307 | "samples" : [ 308 | { 309 | "language" : "en-US", 310 | "taggedText" : "I would like to change my appointment" 311 | }, 312 | { 313 | "language" : "en-US", 314 | "taggedText" : "I would like to modify my appointment" 315 | }, 316 | { 317 | "language" : "en-US", 318 | "taggedText" : "change appointment" 319 | }, 320 | { 321 | "language" : "en-US", 322 | "taggedText" : "modify appointment" 323 | } 324 | ] 325 | }, 326 | { 327 | "uniqueName" : "no-intent", 328 | "actions" : { 329 | "actions" : [ 330 | { 331 | "say" : { 332 | "speech" : "Okay! We will see you soon. Have a great day." 333 | } 334 | }, 335 | { "listen" : true } 336 | ] 337 | }, 338 | "fields" : [ 339 | { 340 | "uniqueName" : "No", 341 | "fieldType" : "No" 342 | } 343 | ], 344 | "samples" : [ 345 | { 346 | "language" : "en-US", 347 | "taggedText" : "{No}" 348 | } 349 | ] 350 | }, 351 | { 352 | "uniqueName" : "welcome-intent", 353 | "actions" : { 354 | "actions" : [ 355 | { 356 | "say" : { 357 | "speech" : "Hi! I am Virtual Assistant, with my help you can book, modify, confirm and cancel an appointment with doctor. How can I help you?" 358 | } 359 | }, 360 | { "listen" : true } 361 | ] 362 | }, 363 | "fields" : [], 364 | "samples" : [ 365 | { 366 | "language" : "en-US", 367 | "taggedText" : "hello there" 368 | }, 369 | { 370 | "language" : "en-US", 371 | "taggedText" : "hi" 372 | }, 373 | { 374 | "language" : "en-US", 375 | "taggedText" : "hello again" 376 | }, 377 | { 378 | "language" : "en-US", 379 | "taggedText" : "hello" 380 | }, 381 | { 382 | "language" : "en-US", 383 | "taggedText" : "hey" 384 | }, 385 | { 386 | "language" : "en-US", 387 | "taggedText" : "hi there" 388 | }, 389 | { 390 | "language" : "en-US", 391 | "taggedText" : "hey there" 392 | }, 393 | { 394 | "language" : "en-US", 395 | "taggedText" : "hello hi" 396 | } 397 | ] 398 | }, 399 | { 400 | "uniqueName" : "yes-intent", 401 | "actions" : { 402 | "actions" : [ 403 | { 404 | "say" : { 405 | "speech" : "Okay, We will send you sms for your appointment. Have a great day." 406 | } 407 | }, 408 | { "listen" : true } 409 | ] 410 | }, 411 | "fields" : [ 412 | { 413 | "uniqueName" : "Yes", 414 | "fieldType" : "Yes" 415 | } 416 | ], 417 | "samples" : [ 418 | { 419 | "language" : "en-US", 420 | "taggedText" : "{Yes}" 421 | } 422 | ] 423 | } 424 | ] 425 | } --------------------------------------------------------------------------------