mirror of
https://github.com/jambonz/verb-specifications.git
synced 2025-12-19 04:47:47 +00:00
feat: first utils
This commit is contained in:
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
1
.eslintignore
Normal file
1
.eslintignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
test/*
|
||||||
126
.eslintrc.json
Normal file
126
.eslintrc.json
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": false,
|
||||||
|
"modules": false
|
||||||
|
},
|
||||||
|
"ecmaVersion": 2020
|
||||||
|
},
|
||||||
|
"plugins": ["promise"],
|
||||||
|
"rules": {
|
||||||
|
"promise/always-return": "error",
|
||||||
|
"promise/no-return-wrap": "error",
|
||||||
|
"promise/param-names": "error",
|
||||||
|
"promise/catch-or-return": "error",
|
||||||
|
"promise/no-native": "off",
|
||||||
|
"promise/no-nesting": "warn",
|
||||||
|
"promise/no-promise-in-callback": "warn",
|
||||||
|
"promise/no-callback-in-promise": "warn",
|
||||||
|
"promise/no-return-in-finally": "warn",
|
||||||
|
|
||||||
|
// Possible Errors
|
||||||
|
// http://eslint.org/docs/rules/#possible-errors
|
||||||
|
"comma-dangle": [2, "only-multiline"],
|
||||||
|
"no-control-regex": 2,
|
||||||
|
"no-debugger": 2,
|
||||||
|
"no-dupe-args": 2,
|
||||||
|
"no-dupe-keys": 2,
|
||||||
|
"no-duplicate-case": 2,
|
||||||
|
"no-empty-character-class": 2,
|
||||||
|
"no-ex-assign": 2,
|
||||||
|
"no-extra-boolean-cast" : 2,
|
||||||
|
"no-extra-parens": [2, "functions"],
|
||||||
|
"no-extra-semi": 2,
|
||||||
|
"no-func-assign": 2,
|
||||||
|
"no-invalid-regexp": 2,
|
||||||
|
"no-irregular-whitespace": 2,
|
||||||
|
"no-negated-in-lhs": 2,
|
||||||
|
"no-obj-calls": 2,
|
||||||
|
"no-proto": 2,
|
||||||
|
"no-unexpected-multiline": 2,
|
||||||
|
"no-unreachable": 2,
|
||||||
|
"use-isnan": 2,
|
||||||
|
"valid-typeof": 2,
|
||||||
|
|
||||||
|
// Best Practices
|
||||||
|
// http://eslint.org/docs/rules/#best-practices
|
||||||
|
"no-fallthrough": 2,
|
||||||
|
"no-octal": 2,
|
||||||
|
"no-redeclare": 2,
|
||||||
|
"no-self-assign": 2,
|
||||||
|
"no-unused-labels": 2,
|
||||||
|
|
||||||
|
// Strict Mode
|
||||||
|
// http://eslint.org/docs/rules/#strict-mode
|
||||||
|
"strict": [2, "never"],
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
// http://eslint.org/docs/rules/#variables
|
||||||
|
"no-delete-var": 2,
|
||||||
|
"no-undef": 2,
|
||||||
|
"no-unused-vars": [2, {"args": "none"}],
|
||||||
|
|
||||||
|
// Node.js and CommonJS
|
||||||
|
// http://eslint.org/docs/rules/#nodejs-and-commonjs
|
||||||
|
"no-mixed-requires": 2,
|
||||||
|
"no-new-require": 2,
|
||||||
|
"no-path-concat": 2,
|
||||||
|
"no-restricted-modules": [2, "sys", "_linklist"],
|
||||||
|
|
||||||
|
// Stylistic Issues
|
||||||
|
// http://eslint.org/docs/rules/#stylistic-issues
|
||||||
|
"comma-spacing": 2,
|
||||||
|
"eol-last": 2,
|
||||||
|
"indent": [2, 2, {"SwitchCase": 1}],
|
||||||
|
"keyword-spacing": 2,
|
||||||
|
"max-len": [2, 120, 2],
|
||||||
|
"new-parens": 2,
|
||||||
|
"no-mixed-spaces-and-tabs": 2,
|
||||||
|
"no-multiple-empty-lines": [2, {"max": 2}],
|
||||||
|
"no-trailing-spaces": [2, {"skipBlankLines": false }],
|
||||||
|
"quotes": [2, "single", "avoid-escape"],
|
||||||
|
"semi": 2,
|
||||||
|
"space-before-blocks": [2, "always"],
|
||||||
|
"space-before-function-paren": [2, "never"],
|
||||||
|
"space-in-parens": [2, "never"],
|
||||||
|
"space-infix-ops": 2,
|
||||||
|
"space-unary-ops": 2,
|
||||||
|
|
||||||
|
// ECMAScript 6
|
||||||
|
// http://eslint.org/docs/rules/#ecmascript-6
|
||||||
|
"arrow-parens": [2, "always"],
|
||||||
|
"arrow-spacing": [2, {"before": true, "after": true}],
|
||||||
|
"constructor-super": 2,
|
||||||
|
"no-class-assign": 2,
|
||||||
|
"no-confusing-arrow": 2,
|
||||||
|
"no-const-assign": 2,
|
||||||
|
"no-dupe-class-members": 2,
|
||||||
|
"no-new-symbol": 2,
|
||||||
|
"no-this-before-super": 2,
|
||||||
|
"prefer-const": 2
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"DTRACE_HTTP_CLIENT_REQUEST" : false,
|
||||||
|
"LTTNG_HTTP_CLIENT_REQUEST" : false,
|
||||||
|
"COUNTER_HTTP_CLIENT_REQUEST" : false,
|
||||||
|
"DTRACE_HTTP_CLIENT_RESPONSE" : false,
|
||||||
|
"LTTNG_HTTP_CLIENT_RESPONSE" : false,
|
||||||
|
"COUNTER_HTTP_CLIENT_RESPONSE" : false,
|
||||||
|
"DTRACE_HTTP_SERVER_REQUEST" : false,
|
||||||
|
"LTTNG_HTTP_SERVER_REQUEST" : false,
|
||||||
|
"COUNTER_HTTP_SERVER_REQUEST" : false,
|
||||||
|
"DTRACE_HTTP_SERVER_RESPONSE" : false,
|
||||||
|
"LTTNG_HTTP_SERVER_RESPONSE" : false,
|
||||||
|
"COUNTER_HTTP_SERVER_RESPONSE" : false,
|
||||||
|
"DTRACE_NET_STREAM_END" : false,
|
||||||
|
"LTTNG_NET_STREAM_END" : false,
|
||||||
|
"COUNTER_NET_SERVER_CONNECTION_CLOSE" : false,
|
||||||
|
"DTRACE_NET_SERVER_CONNECTION" : false,
|
||||||
|
"LTTNG_NET_SERVER_CONNECTION" : false,
|
||||||
|
"COUNTER_NET_SERVER_CONNECTION" : false
|
||||||
|
}
|
||||||
|
}
|
||||||
49
.gitignore
vendored
Normal file
49
.gitignore
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
run-tests.sh
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
|
||||||
|
# github pages site
|
||||||
|
_site
|
||||||
|
|
||||||
|
#transient test cases
|
||||||
|
examples/nosave.*.js
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul and nyc
|
||||||
|
coverage
|
||||||
|
.nyc_output/
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directory
|
||||||
|
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
examples/*
|
||||||
|
|
||||||
|
create_db.sql
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
.env.*
|
||||||
|
.env
|
||||||
|
|
||||||
|
test/postgres-data
|
||||||
|
db/remove-account.sh
|
||||||
7
README.md
Normal file
7
README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# verb-specificiations
|
||||||
|
Jambonz Verb Specification Utilities
|
||||||
|
|
||||||
|
#### Running the test suite
|
||||||
|
```
|
||||||
|
npm test
|
||||||
|
```
|
||||||
113
jambonz-app-json-validation.js
Normal file
113
jambonz-app-json-validation.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
const assert = require('assert');
|
||||||
|
const _specData = require('./specs');
|
||||||
|
const specs = new Map();
|
||||||
|
for (const key in _specData) { specs.set(key, _specData[key]); }
|
||||||
|
|
||||||
|
function normalizeJambones(logger, obj) {
|
||||||
|
if (!Array.isArray(obj)) {
|
||||||
|
throw new Error('malformed jambonz payload: must be array');
|
||||||
|
}
|
||||||
|
const document = [];
|
||||||
|
for (const tdata of obj) {
|
||||||
|
if (typeof tdata !== 'object') throw new Error('malformed jambonz payload: must be array of objects');
|
||||||
|
if ('verb' in tdata) {
|
||||||
|
// {verb: 'say', text: 'foo..bar'..}
|
||||||
|
const name = tdata.verb;
|
||||||
|
const o = {};
|
||||||
|
Object.keys(tdata)
|
||||||
|
.filter((k) => k !== 'verb')
|
||||||
|
.forEach((k) => o[k] = tdata[k]);
|
||||||
|
const o2 = {};
|
||||||
|
o2[name] = o;
|
||||||
|
document.push(o2);
|
||||||
|
}
|
||||||
|
else if (Object.keys(tdata).length === 1) {
|
||||||
|
// {'say': {..}}
|
||||||
|
document.push(tdata);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.info(tdata, 'malformed jambonz payload: missing verb property');
|
||||||
|
throw new Error('malformed jambonz payload: missing verb property');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.debug({ document }, `normalizeJambones: returning document with ${document.length} tasks`);
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate(logger, obj) {
|
||||||
|
normalizeJambones(logger, obj).map((tdata) => {
|
||||||
|
const keys = Object.keys(tdata);
|
||||||
|
const name = keys[0];
|
||||||
|
const data = tdata[name];
|
||||||
|
validateVerb(name, data, logger);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateVerb(name, data, logger) {
|
||||||
|
logger.debug(`validating ${name} with data ${JSON.stringify(data)}`);
|
||||||
|
// validate the instruction is supported
|
||||||
|
if (!specs.has(name)) throw new Error(`invalid instruction: ${name}`);
|
||||||
|
|
||||||
|
// check type of each element and make sure required elements are present
|
||||||
|
const specData = specs.get(name);
|
||||||
|
let required = specData.required || [];
|
||||||
|
for (const dKey in data) {
|
||||||
|
if (dKey in specData.properties) {
|
||||||
|
const dVal = data[dKey];
|
||||||
|
const dSpec = specData.properties[dKey];
|
||||||
|
logger.debug(`Task:validate validating property ${dKey} with value ${JSON.stringify(dVal)}`);
|
||||||
|
|
||||||
|
if (typeof dSpec === 'string' && dSpec === 'array') {
|
||||||
|
if (!Array.isArray(dVal)) throw new Error(`${name}: property ${dKey} is not an array`);
|
||||||
|
}
|
||||||
|
else if (typeof dSpec === 'string' && dSpec.includes('|')) {
|
||||||
|
const types = dSpec.split('|').map((t) => t.trim());
|
||||||
|
if (!types.includes(typeof dVal) && !(types.includes('array') && Array.isArray(dVal))) {
|
||||||
|
throw new Error(`${name}: property ${dKey} has invalid data type, must be one of ${types}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (typeof dSpec === 'string' && ['number', 'string', 'object', 'boolean'].includes(dSpec)) {
|
||||||
|
// simple types
|
||||||
|
if (typeof dVal !== specData.properties[dKey]) {
|
||||||
|
throw new Error(`${name}: property ${dKey} has invalid data type`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Array.isArray(dSpec) && dSpec[0].startsWith('#')) {
|
||||||
|
const name = dSpec[0].slice(1);
|
||||||
|
for (const item of dVal) {
|
||||||
|
validateVerb(name, item, logger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (typeof dSpec === 'object') {
|
||||||
|
// complex types
|
||||||
|
const type = dSpec.type;
|
||||||
|
assert.ok(['number', 'string', 'object', 'boolean'].includes(type),
|
||||||
|
`invalid or missing type in spec ${JSON.stringify(dSpec)}`);
|
||||||
|
if (type === 'string' && dSpec.enum) {
|
||||||
|
assert.ok(Array.isArray(dSpec.enum), `enum must be an array ${JSON.stringify(dSpec.enum)}`);
|
||||||
|
if (!dSpec.enum.includes(dVal)) {
|
||||||
|
throw new Error(`invalid value ${dVal} must be one of ${dSpec.enum}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (typeof dSpec === 'string' && dSpec.startsWith('#')) {
|
||||||
|
// reference to another datatype (i.e. nested type)
|
||||||
|
const name = dSpec.slice(1);
|
||||||
|
//const obj = {};
|
||||||
|
//obj[name] = dVal;
|
||||||
|
validateVerb(name, dVal, logger);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert.ok(0, `invalid spec ${JSON.stringify(dSpec)}`);
|
||||||
|
}
|
||||||
|
required = required.filter((item) => item !== dKey);
|
||||||
|
}
|
||||||
|
else if (dKey === '_') {
|
||||||
|
/* no op: allow arbitrary info to be carried here, used by conference e.g in transfer */
|
||||||
|
}
|
||||||
|
else throw new Error(`${name}: unknown property ${dKey}`);
|
||||||
|
}
|
||||||
|
if (required.length > 0) throw new Error(`${name}: missing value for ${required}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { normalizeJambones, validate };
|
||||||
4277
package-lock.json
generated
Normal file
4277
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
package.json
Normal file
27
package.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "verb-specifications",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Jambonz Verb Specification Utilities",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node test/"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/jambonz/verb-specifications.git"
|
||||||
|
},
|
||||||
|
"author": "Dave Horton",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/jambonz/verb-specifications/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/jambonz/verb-specifications#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"eslint-plugin-promise": "^4.3.1",
|
||||||
|
"tape": "^5.6.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"pino": "^8.8.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
777
specs.json
Normal file
777
specs.json
Normal file
@@ -0,0 +1,777 @@
|
|||||||
|
{
|
||||||
|
"sip:decline": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"status": "number",
|
||||||
|
"reason": "string",
|
||||||
|
"headers": "object"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"status"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sip:request": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"method": "string",
|
||||||
|
"body": "string",
|
||||||
|
"headers": "object",
|
||||||
|
"actionHook": "object|string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"method"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sip:refer": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"referTo": "string",
|
||||||
|
"referredBy": "string",
|
||||||
|
"headers": "object",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"eventHook": "object|string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"referTo"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"synthesizer": "#synthesizer",
|
||||||
|
"recognizer": "#recognizer",
|
||||||
|
"bargeIn": "#bargeIn",
|
||||||
|
"record": "#recordOptions",
|
||||||
|
"listen": "#listenOptions",
|
||||||
|
"amd": "#amd",
|
||||||
|
"notifyEvents": "boolean"
|
||||||
|
},
|
||||||
|
"required": []
|
||||||
|
},
|
||||||
|
"listenOptions": {
|
||||||
|
"properties": {
|
||||||
|
"enable": "boolean",
|
||||||
|
"url": "string",
|
||||||
|
"sampleRate": "number",
|
||||||
|
"wsAuth": "#auth",
|
||||||
|
"mixType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["mono", "stereo", "mixed"]
|
||||||
|
},
|
||||||
|
"metadata": "object"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"enable"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"bargeIn": {
|
||||||
|
"properties": {
|
||||||
|
"enable": "boolean",
|
||||||
|
"sticky": "boolean",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"input": "array",
|
||||||
|
"finishOnKey": "string",
|
||||||
|
"numDigits": "number",
|
||||||
|
"minDigits": "number",
|
||||||
|
"maxDigits": "number",
|
||||||
|
"interDigitTimeout": "number",
|
||||||
|
"dtmfBargein": "boolean",
|
||||||
|
"minBargeinWordCount": "number"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"enable"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dequeue": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"name": "string",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"timeout": "number",
|
||||||
|
"beep": "boolean"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"enqueue": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"name": "string",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"waitHook": "object|string",
|
||||||
|
"_": "object"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"leave": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hangup": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"headers": "object"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"play": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"url": "string|array",
|
||||||
|
"loop": "number|string",
|
||||||
|
"earlyMedia": "boolean",
|
||||||
|
"seekOffset": "number|string",
|
||||||
|
"timeoutSecs": "number|string",
|
||||||
|
"actionHook": "object|string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"url"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"say": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"text": "string|array",
|
||||||
|
"loop": "number|string",
|
||||||
|
"synthesizer": "#synthesizer",
|
||||||
|
"earlyMedia": "boolean",
|
||||||
|
"disableTtsCache": "boolean"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"text"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gather": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"finishOnKey": "string",
|
||||||
|
"input": "array",
|
||||||
|
"numDigits": "number",
|
||||||
|
"minDigits": "number",
|
||||||
|
"maxDigits": "number",
|
||||||
|
"interDigitTimeout": "number",
|
||||||
|
"partialResultHook": "object|string",
|
||||||
|
"speechTimeout": "number",
|
||||||
|
"listenDuringPrompt": "boolean",
|
||||||
|
"dtmfBargein": "boolean",
|
||||||
|
"bargein": "boolean",
|
||||||
|
"minBargeinWordCount": "number",
|
||||||
|
"timeout": "number",
|
||||||
|
"recognizer": "#recognizer",
|
||||||
|
"play": "#play",
|
||||||
|
"say": "#say"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"conference": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"name": "string",
|
||||||
|
"beep": "boolean",
|
||||||
|
"startConferenceOnEnter": "boolean",
|
||||||
|
"endConferenceOnExit": "boolean",
|
||||||
|
"maxParticipants": "number",
|
||||||
|
"joinMuted": "boolean",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"waitHook": "object|string",
|
||||||
|
"statusEvents": "array",
|
||||||
|
"statusHook": "object|string",
|
||||||
|
"enterHook": "object|string",
|
||||||
|
"record": "#record"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dial": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"answerOnBridge": "boolean",
|
||||||
|
"callerId": "string",
|
||||||
|
"confirmHook": "object|string",
|
||||||
|
"referHook": "object|string",
|
||||||
|
"dialMusic": "string",
|
||||||
|
"dtmfCapture": "object",
|
||||||
|
"dtmfHook": "object|string",
|
||||||
|
"headers": "object",
|
||||||
|
"listen": "#listen",
|
||||||
|
"target": ["#target"],
|
||||||
|
"timeLimit": "number",
|
||||||
|
"timeout": "number",
|
||||||
|
"proxy": "string",
|
||||||
|
"transcribe": "#transcribe",
|
||||||
|
"amd": "#amd"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"target"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dialogflow": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"credentials": "object|string",
|
||||||
|
"project": "string",
|
||||||
|
"environment": "string",
|
||||||
|
"region": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["europe-west1", "europe-west2", "australia-southeast1", "asia-northeast1"]
|
||||||
|
},
|
||||||
|
"lang": "string",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"eventHook": "object|string",
|
||||||
|
"events": "[string]",
|
||||||
|
"welcomeEvent": "string",
|
||||||
|
"welcomeEventParams": "object",
|
||||||
|
"noInputTimeout": "number",
|
||||||
|
"noInputEvent": "string",
|
||||||
|
"passDtmfAsTextInput": "boolean",
|
||||||
|
"thinkingMusic": "string",
|
||||||
|
"tts": "#synthesizer",
|
||||||
|
"bargein": "boolean"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"project",
|
||||||
|
"credentials",
|
||||||
|
"lang"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dtmf": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"dtmf": "string",
|
||||||
|
"duration": "number"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"dtmf"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lex": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"botId": "string",
|
||||||
|
"botAlias": "string",
|
||||||
|
"credentials": "object",
|
||||||
|
"region": "string",
|
||||||
|
"locale": "string",
|
||||||
|
"intent": "#lexIntent",
|
||||||
|
"welcomeMessage": "string",
|
||||||
|
"metadata": "object",
|
||||||
|
"bargein": "boolean",
|
||||||
|
"passDtmf": "boolean",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"eventHook": "object|string",
|
||||||
|
"noInputTimeout": "number",
|
||||||
|
"tts": "#synthesizer"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"botId",
|
||||||
|
"botAlias",
|
||||||
|
"region",
|
||||||
|
"credentials"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"listen": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"auth": "#auth",
|
||||||
|
"finishOnKey": "string",
|
||||||
|
"maxLength": "number",
|
||||||
|
"metadata": "object",
|
||||||
|
"mixType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["mono", "stereo", "mixed"]
|
||||||
|
},
|
||||||
|
"passDtmf": "boolean",
|
||||||
|
"playBeep": "boolean",
|
||||||
|
"disableBidirectionalAudio": "boolean",
|
||||||
|
"sampleRate": "number",
|
||||||
|
"timeout": "number",
|
||||||
|
"transcribe": "#transcribe",
|
||||||
|
"url": "string",
|
||||||
|
"wsAuth": "#auth",
|
||||||
|
"earlyMedia": "boolean"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"url"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"carrier": "string",
|
||||||
|
"account_sid": "string",
|
||||||
|
"message_sid": "string",
|
||||||
|
"to": "string",
|
||||||
|
"from": "string",
|
||||||
|
"text": "string",
|
||||||
|
"media": "string|array",
|
||||||
|
"actionHook": "object|string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"to",
|
||||||
|
"from"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pause": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"length": "number"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"length"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rasa": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"url": "string",
|
||||||
|
"recognizer": "#recognizer",
|
||||||
|
"tts": "#synthesizer",
|
||||||
|
"prompt": "string",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"eventHook": "object|string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"url"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"record": {
|
||||||
|
"properties": {
|
||||||
|
"path": "string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"path"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"recordOptions": {
|
||||||
|
"properties": {
|
||||||
|
"action": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["startCallRecording", "stopCallRecording", "pauseCallRecording", "resumeCallRecording"]
|
||||||
|
},
|
||||||
|
"recordingID": "string",
|
||||||
|
"siprecServerURL": "string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"action"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"redirect": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"actionHook": "object|string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"actionHook"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"rest:dial": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"account_sid": "string",
|
||||||
|
"application_sid": "string",
|
||||||
|
"call_hook": "object|string",
|
||||||
|
"call_status_hook": "object|string",
|
||||||
|
"from": "string",
|
||||||
|
"fromHost": "string",
|
||||||
|
"speech_synthesis_vendor": "string",
|
||||||
|
"speech_synthesis_voice": "string",
|
||||||
|
"speech_synthesis_language": "string",
|
||||||
|
"speech_recognizer_vendor": "string",
|
||||||
|
"speech_recognizer_language": "string",
|
||||||
|
"tag": "object",
|
||||||
|
"to": "#target",
|
||||||
|
"headers": "object",
|
||||||
|
"timeout": "number"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"call_hook",
|
||||||
|
"from",
|
||||||
|
"to"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tag": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"data": "object"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"data"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"transcribe": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"transcriptionHook": "string",
|
||||||
|
"recognizer": "#recognizer",
|
||||||
|
"earlyMedia": "boolean"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"recognizer"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["phone", "sip", "user", "teams"]
|
||||||
|
},
|
||||||
|
"confirmHook": "object|string",
|
||||||
|
"method": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["GET", "POST"]
|
||||||
|
},
|
||||||
|
"headers": "object",
|
||||||
|
"from": "#dialFrom",
|
||||||
|
"name": "string",
|
||||||
|
"number": "string",
|
||||||
|
"sipUri": "string",
|
||||||
|
"auth": "#auth",
|
||||||
|
"vmail": "boolean",
|
||||||
|
"tenant": "string",
|
||||||
|
"trunk": "string",
|
||||||
|
"overrideTo": "string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dialFrom": {
|
||||||
|
"properties": {
|
||||||
|
"user": "string",
|
||||||
|
"host": "string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"properties": {
|
||||||
|
"username": "string",
|
||||||
|
"password": "string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"username",
|
||||||
|
"password"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"synthesizer": {
|
||||||
|
"properties": {
|
||||||
|
"vendor": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["google", "aws", "polly", "microsoft", "nuance", "ibm", "default"]
|
||||||
|
},
|
||||||
|
"language": "string",
|
||||||
|
"voice": "string",
|
||||||
|
"engine": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["standard", "neural"]
|
||||||
|
},
|
||||||
|
"gender": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["MALE", "FEMALE", "NEUTRAL"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"vendor"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"recognizer": {
|
||||||
|
"properties": {
|
||||||
|
"vendor": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["google", "aws", "microsoft", "nuance", "deepgram", "ibm", "default"]
|
||||||
|
},
|
||||||
|
"language": "string",
|
||||||
|
"vad": "#vad",
|
||||||
|
"hints": "array",
|
||||||
|
"hintsBoost": "number",
|
||||||
|
"altLanguages": "array",
|
||||||
|
"profanityFilter": "boolean",
|
||||||
|
"interim": "boolean",
|
||||||
|
"singleUtterance": "boolean",
|
||||||
|
"dualChannel": "boolean",
|
||||||
|
"separateRecognitionPerChannel": "boolean",
|
||||||
|
"punctuation": "boolean",
|
||||||
|
"enhancedModel": "boolean",
|
||||||
|
"words": "boolean",
|
||||||
|
"diarization": "boolean",
|
||||||
|
"diarizationMinSpeakers": "number",
|
||||||
|
"diarizationMaxSpeakers": "number",
|
||||||
|
"interactionType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"unspecified",
|
||||||
|
"discussion",
|
||||||
|
"presentation",
|
||||||
|
"phone_call",
|
||||||
|
"voicemail",
|
||||||
|
"voice_search",
|
||||||
|
"voice_command",
|
||||||
|
"dictation"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"naicsCode": "number",
|
||||||
|
"identifyChannels": "boolean",
|
||||||
|
"vocabularyName": "string",
|
||||||
|
"vocabularyFilterName": "string",
|
||||||
|
"filterMethod": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"remove",
|
||||||
|
"mask",
|
||||||
|
"tag"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"model": "string",
|
||||||
|
"outputFormat": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"simple",
|
||||||
|
"detailed"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"profanityOption": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"masked",
|
||||||
|
"removed",
|
||||||
|
"raw"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"requestSnr": "boolean",
|
||||||
|
"initialSpeechTimeoutMs": "number",
|
||||||
|
"azureServiceEndpoint": "string",
|
||||||
|
"azureSttEndpointId": "string",
|
||||||
|
"asrDtmfTerminationDigit": "string",
|
||||||
|
"asrTimeout": "number",
|
||||||
|
"nuanceOptions": "#nuanceOptions",
|
||||||
|
"deepgramOptions": "#deepgramOptions",
|
||||||
|
"ibmOptions": "#ibmOptions"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"vendor"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ibmOptions": {
|
||||||
|
"properties": {
|
||||||
|
"sttApiKey": "string",
|
||||||
|
"sttRegion": "string",
|
||||||
|
"ttsApiKey": "string",
|
||||||
|
"ttsRegion": "string",
|
||||||
|
"instanceId": "string",
|
||||||
|
"model": "string",
|
||||||
|
"languageCustomizationId": "string",
|
||||||
|
"acousticCustomizationId": "string",
|
||||||
|
"baseModelVersion": "string",
|
||||||
|
"watsonMetadata": "string",
|
||||||
|
"watsonLearningOptOut": "boolean"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"deepgramOptions": {
|
||||||
|
"properties": {
|
||||||
|
"apiKey": "string",
|
||||||
|
"tier": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"enhanced",
|
||||||
|
"base"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"model": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"general",
|
||||||
|
"meeting",
|
||||||
|
"phonecall",
|
||||||
|
"voicemail",
|
||||||
|
"finance",
|
||||||
|
"conversationalai",
|
||||||
|
"video",
|
||||||
|
"custom"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"customModel": "string",
|
||||||
|
"version": "string",
|
||||||
|
"punctuate": "boolean",
|
||||||
|
"profanityFilter": "boolean",
|
||||||
|
"redact": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"pci",
|
||||||
|
"numbers",
|
||||||
|
"true",
|
||||||
|
"ssn"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"diarize": "boolean",
|
||||||
|
"diarizeVersion": "string",
|
||||||
|
"ner": "boolean",
|
||||||
|
"multichannel": "boolean",
|
||||||
|
"alternatives": "number",
|
||||||
|
"numerals": "boolean",
|
||||||
|
"search": "array",
|
||||||
|
"replace": "array",
|
||||||
|
"keywords": "array",
|
||||||
|
"endpointing": "boolean",
|
||||||
|
"vadTurnoff": "number",
|
||||||
|
"tag": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nuanceOptions": {
|
||||||
|
"properties": {
|
||||||
|
"clientId": "string",
|
||||||
|
"secret": "string",
|
||||||
|
"kryptonEndpoint": "string",
|
||||||
|
"topic": "string",
|
||||||
|
"utteranceDetectionMode": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"single",
|
||||||
|
"multiple",
|
||||||
|
"disabled"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"punctuation": "boolean",
|
||||||
|
"profanityFilter": "boolean",
|
||||||
|
"includeTokenization": "boolean",
|
||||||
|
"discardSpeakerAdaptation": "boolean",
|
||||||
|
"suppressCallRecording": "boolean",
|
||||||
|
"maskLoadFailures": "boolean",
|
||||||
|
"suppressInitialCapitalization": "boolean",
|
||||||
|
"allowZeroBaseLmWeight": "boolean",
|
||||||
|
"filterWakeupWord": "boolean",
|
||||||
|
"resultType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"final",
|
||||||
|
"partial",
|
||||||
|
"immutable_partial"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"noInputTimeoutMs": "number",
|
||||||
|
"recognitionTimeoutMs": "number",
|
||||||
|
"utteranceEndSilenceMs": "number",
|
||||||
|
"maxHypotheses": "number",
|
||||||
|
"speechDomain": "string",
|
||||||
|
"formatting": "#formatting",
|
||||||
|
"clientData": "object",
|
||||||
|
"userId": "string",
|
||||||
|
"speechDetectionSensitivity": "number",
|
||||||
|
"resources": ["#resource"]
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"properties": {
|
||||||
|
"externalReference": "#resourceReference",
|
||||||
|
"inlineWordset": "string",
|
||||||
|
"builtin": "string",
|
||||||
|
"inlineGrammar": "string",
|
||||||
|
"wakeupWord": "[string]",
|
||||||
|
"weightName": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"defaultWeight",
|
||||||
|
"lowest",
|
||||||
|
"low",
|
||||||
|
"medium",
|
||||||
|
"high",
|
||||||
|
"highest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"weightValue": "number",
|
||||||
|
"reuse": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"undefined_reuse",
|
||||||
|
"low_reuse",
|
||||||
|
"high_reuse"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"resourceReference": {
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"undefined_resource_type",
|
||||||
|
"wordset",
|
||||||
|
"compiled_wordset",
|
||||||
|
"domain_lm",
|
||||||
|
"speaker_profile",
|
||||||
|
"grammar",
|
||||||
|
"settings"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"uri": "string",
|
||||||
|
"maxLoadFailures": "boolean",
|
||||||
|
"requestTimeoutMs": "number",
|
||||||
|
"headers": "object"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"formatting": {
|
||||||
|
"properties": {
|
||||||
|
"scheme": "string",
|
||||||
|
"options": "object"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"scheme",
|
||||||
|
"options"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lexIntent": {
|
||||||
|
"properties": {
|
||||||
|
"name": "string",
|
||||||
|
"slots": "object"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"vad": {
|
||||||
|
"properties": {
|
||||||
|
"enable": "boolean",
|
||||||
|
"voiceMs": "number",
|
||||||
|
"mode": "number"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"enable"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"amd": {
|
||||||
|
"properties": {
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"thresholdWordCount": "number",
|
||||||
|
"timers": "#amdTimers",
|
||||||
|
"recognizer": "#recognizer"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"actionHook"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"amdTimers": {
|
||||||
|
"properties": {
|
||||||
|
"noSpeechTimeoutMs": "number",
|
||||||
|
"decisionTimeoutMs": "number",
|
||||||
|
"toneTimeoutMs": "number",
|
||||||
|
"greetingCompletionTimeoutMs": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
test/index.js
Normal file
1
test/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
require('./jambonz-verb-test');
|
||||||
267
test/jambonz-verb-test.js
Normal file
267
test/jambonz-verb-test.js
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
const test = require('tape');
|
||||||
|
const logger = require('pino')({level: process.env.JAMBONES_LOGLEVEL || 'error'});
|
||||||
|
const { validate } = require('../jambonz-app-json-validation');
|
||||||
|
|
||||||
|
test("validate correct verbs", async (t) => {
|
||||||
|
|
||||||
|
verbs = [
|
||||||
|
{
|
||||||
|
"verb": "play",
|
||||||
|
"url": "https://example.com/example.mp3",
|
||||||
|
"timeoutSecs": 10,
|
||||||
|
"seekOffset": 8000,
|
||||||
|
"actionHook": "/play/action"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "conference",
|
||||||
|
"name": "test",
|
||||||
|
"beep": true,
|
||||||
|
"startConferenceOnEnter": false,
|
||||||
|
"waitHook": "/confWait",
|
||||||
|
"enterHook": "/confEnter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "config",
|
||||||
|
"synthesizer": {
|
||||||
|
"voice": "Jenny",
|
||||||
|
"vendor": "google"
|
||||||
|
},
|
||||||
|
"recognizer": {
|
||||||
|
"vendor": "google",
|
||||||
|
"language": "de-DE"
|
||||||
|
},
|
||||||
|
"bargeIn": {
|
||||||
|
"enable": true,
|
||||||
|
"input" : ["speech"],
|
||||||
|
"actionHook": "/userInput"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "dequeue",
|
||||||
|
"name": "support",
|
||||||
|
"beep": true,
|
||||||
|
"timeout": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "dial",
|
||||||
|
"actionHook": "/outdial",
|
||||||
|
"callerId": "+16173331212",
|
||||||
|
"answerOnBridge": true,
|
||||||
|
"dtmfCapture": ["*2", "*3"],
|
||||||
|
"dtmfHook": {
|
||||||
|
"url": "/dtmf",
|
||||||
|
"method": "GET"
|
||||||
|
},
|
||||||
|
"amd": {
|
||||||
|
"actionHook": "/answeringMachineDetection",
|
||||||
|
|
||||||
|
},
|
||||||
|
"target": [
|
||||||
|
{
|
||||||
|
"type": "phone",
|
||||||
|
"number": "+15083084809",
|
||||||
|
"trunk": "Twilio"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sip",
|
||||||
|
"sipUri": "sip:1617333456@sip.trunk1.com",
|
||||||
|
"auth": {
|
||||||
|
"username": "foo",
|
||||||
|
"password": "bar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "user",
|
||||||
|
"name": "spike@sip.example.com"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "dialogflow",
|
||||||
|
"project": "ai-in-rtc-drachtio-tsjjpn",
|
||||||
|
"lang": "en-US",
|
||||||
|
"credentials": "{\"type\": \"service_account\",\"project_id\": \"prj..",
|
||||||
|
"welcomeEvent": "welcome",
|
||||||
|
"eventHook": "/dialogflow-event",
|
||||||
|
"actionHook": "/dialogflow-action"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "dtmf",
|
||||||
|
"dtmf": "0276",
|
||||||
|
"duration": 250
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "enqueue",
|
||||||
|
"name": "support",
|
||||||
|
"actionHook": "/queue-action",
|
||||||
|
"waitHook": "/queue-wait"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "gather",
|
||||||
|
"actionHook": "http://example.com/collect",
|
||||||
|
"input": ["digits", "speech"],
|
||||||
|
"bargein": true,
|
||||||
|
"dtmfBargein": true,
|
||||||
|
"finishOnKey": "#",
|
||||||
|
"numDigits": 5,
|
||||||
|
"timeout": 8,
|
||||||
|
"recognizer": {
|
||||||
|
"vendor": "google",
|
||||||
|
"language": "en-US",
|
||||||
|
"hints": ["sales", "support"],
|
||||||
|
"hintsBoost": 10
|
||||||
|
},
|
||||||
|
"say": {
|
||||||
|
"text": "To speak to Sales press 1 or say Sales. To speak to customer support press 2 or say Support",
|
||||||
|
"synthesizer": {
|
||||||
|
"vendor": "google",
|
||||||
|
"language": "en-US"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "hangup",
|
||||||
|
"headers": {
|
||||||
|
"X-Reason" : "maximum call duration exceeded"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "leave"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "lex",
|
||||||
|
"botId": "MTLNerCD9L",
|
||||||
|
"botAlias": "z5yY1iYykE",
|
||||||
|
"region": "us-east-1",
|
||||||
|
"locale": "en_US",
|
||||||
|
"credentials": {
|
||||||
|
"accessKey": "XXXX",
|
||||||
|
"secretAccessKey": "YYYY"
|
||||||
|
},
|
||||||
|
"passDtmf": true,
|
||||||
|
"intent": {"name":"BookHotel"},
|
||||||
|
"metadata": {
|
||||||
|
"slots": {
|
||||||
|
"Location": "Los Angeles"
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"callerId": "+15083084909",
|
||||||
|
"customerName": "abc company"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tts": {
|
||||||
|
"vendor": "google",
|
||||||
|
"language": "en-US",
|
||||||
|
"voice": "en-US-Wavenet-C"
|
||||||
|
},
|
||||||
|
"eventHook": "/lex-events"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "listen",
|
||||||
|
"url": "wss://myrecorder.example.com/calls",
|
||||||
|
"mixType" : "stereo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "message",
|
||||||
|
"to": "15083084809",
|
||||||
|
"from": "16173334567",
|
||||||
|
"text": "Your one-time passcode is 1234",
|
||||||
|
"actionHook": "/sms/action"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "pause",
|
||||||
|
"length": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "play",
|
||||||
|
"url": "https://example.com/example.mp3",
|
||||||
|
"timeoutSecs": 10,
|
||||||
|
"seekOffset": 8000,
|
||||||
|
"actionHook": "/play/action"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "rasa",
|
||||||
|
"url": "http://my-assitant.acme.com/webhooks/rest/webhook?token=foobarbazzle",
|
||||||
|
"prompt": "Hello there! What can I do for you today?",
|
||||||
|
"eventHook": "/rasa/event",
|
||||||
|
"actionHook": "/rasa/action"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "redirect",
|
||||||
|
"actionHook": "/connectToSales",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "say",
|
||||||
|
"text": "hi there!",
|
||||||
|
"synthesizer" : {
|
||||||
|
"vendor": "google",
|
||||||
|
"language": "en-US"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "sip:decline",
|
||||||
|
"status": 480,
|
||||||
|
"reason": "Gone Fishing",
|
||||||
|
"headers" : {
|
||||||
|
"Retry-After": 1800
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "sip:request",
|
||||||
|
"method": "INFO",
|
||||||
|
"headers": {
|
||||||
|
"X-Metadata": "my sip metadata"
|
||||||
|
},
|
||||||
|
"actionHook": "/info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "sip:refer",
|
||||||
|
"referTo": "+15083084809",
|
||||||
|
"actionHook": "/action"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "tag",
|
||||||
|
"data": {
|
||||||
|
"foo": "bar",
|
||||||
|
"counter": 100,
|
||||||
|
"list": [1, 2, "three"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "transcribe",
|
||||||
|
"transcriptionHook": "http://example.com/transcribe",
|
||||||
|
"recognizer": {
|
||||||
|
"vendor": "google",
|
||||||
|
"language" : "en-US",
|
||||||
|
"interim": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
try {
|
||||||
|
validate(logger, verbs);
|
||||||
|
t.ok(1 == 1,'successfully validate verbs');
|
||||||
|
} catch(err) {
|
||||||
|
t.fail('validate should not fail here. with reason: ' + err);
|
||||||
|
}
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('invalid test', async (t) => {
|
||||||
|
verbs = [
|
||||||
|
{
|
||||||
|
"verb": "play",
|
||||||
|
"timeoutSecs": 10,
|
||||||
|
"seekOffset": 8000,
|
||||||
|
"actionHook": "/play/action"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
try {
|
||||||
|
validate(logger, verbs);
|
||||||
|
t.fail('validate should not fail here. with reason: ' + err);
|
||||||
|
} catch(err) {
|
||||||
|
t.ok(1 == 1,'successfully validate verbs');
|
||||||
|
}
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user