voxa-cli
Version:
The Voxa CLI tools
320 lines • 13.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/*
* Copyright (c) 2018 Rain Agency <contact@rain.agency>
* Author: Rain Agency <contact@rain.agency>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* tslint:disable:no-empty no-submodule-imports */
const lodash_1 = __importDefault(require("lodash"));
const v5_1 = __importDefault(require("uuid/v5"));
const DialogflowDefault_1 = require("./DialogflowDefault");
const Schema_1 = require("./Schema");
const NAMESPACE = "dialogflow";
// https://developers.google.com/actions/localization/languages-locales
const LOCALES = lodash_1.default([
"en-US",
"en-AU",
"en-CA",
"en-IN",
"en-GB",
"de-DE",
"fr-FR",
"fr-CA",
"ja-JP",
"ko-KR",
"es-ES",
"es-US",
"es-MX",
"pt-BR",
"it-IT",
"ru-RU",
"hi-IN",
"th-TH",
"id-ID",
"da-DK",
"no-NO",
"nl-NL",
"sv-SE",
"ko-KR",
"ru-RU",
"hi-IN",
"th-TH",
"id-ID"
])
.uniq()
.value();
const LANG_BUT_LOCALE = lodash_1.default(LOCALES)
.map(item => item.split("-")[0]) // es, en, du etc.
.uniq()
.value();
const AVAILABLE_LOCALES = LANG_BUT_LOCALE.concat(LOCALES);
class DialogflowSchema extends Schema_1.Schema {
constructor(voxaSheets, interactionOptions) {
super(NAMESPACE, AVAILABLE_LOCALES, voxaSheets, interactionOptions);
this.environment = "staging";
this.builtIntents = [];
}
validate() { }
build(locale, environment) {
this.buildIntent(locale, environment);
this.buildUtterances(locale, environment);
this.buildEntities(locale, environment);
this.buildPackage(environment);
this.buildAgent(locale, environment);
}
buildPackage(environment) {
const file = {
path: this.buildFilePath(environment, "package.json"),
content: {
version: "1.0.0"
}
};
this.fileContent.push(file);
}
getLocale(locale) {
locale = locale.toLowerCase();
const localesNotAttachedToParentLang = ["pt-br"];
if (localesNotAttachedToParentLang.find(item => locale === item)) {
return locale;
}
return locale.split("-")[0];
}
buildAgent(locale, environment) {
const intents = this.builtIntents;
const invocation = lodash_1.default.find(this.invocations, { locale, environment });
const invocationName = lodash_1.default.get(invocation, "name", "Skill with no name");
const intentsByPlatformAndEnvironments = this.intentsByPlatformAndEnvironments(locale, environment);
const startIntents = lodash_1.default(intentsByPlatformAndEnvironments)
.filter({ startIntent: true })
.map(intent => {
const startIntent = findIntent(intent.name, intents);
if (startIntent) {
return {
intentId: lodash_1.default.get(startIntent, "id"),
signInRequired: !!lodash_1.default.get(startIntent, "signInRequired")
};
}
})
.compact()
.value();
const endIntentIds = lodash_1.default(intentsByPlatformAndEnvironments)
.filter({ endIntent: true })
.map(intent => {
const filteredIntent = findIntent(intent.name, intents);
return lodash_1.default.get(filteredIntent, "id");
})
.compact()
.value();
const supportedLanguages = lodash_1.default(this.invocations)
.filter({ environment })
.map("locale")
.uniq()
.value();
const language = this.getLocale(locale);
const agent = lodash_1.default.merge(lodash_1.default.cloneDeep(DialogflowDefault_1.AGENT), lodash_1.default.cloneDeep(this.mergeManifest(environment)), lodash_1.default.cloneDeep({
description: invocationName,
language,
supportedLanguages,
googleAssistant: { project: lodash_1.default.kebabCase(invocationName), startIntents, endIntentIds }
}));
const file = {
path: this.buildFilePath(environment, "agent.json"),
content: agent
};
this.fileContent.push(file);
}
buildUtterances(locale, environment) {
const intentsByPlatformAndEnvironments = this.intentsByPlatformAndEnvironments(locale, environment);
locale = this.getLocale(locale);
intentsByPlatformAndEnvironments.map((rawIntent) => {
let { name, samples, events } = rawIntent;
const { slotsDefinition } = rawIntent;
name = name.replace("AMAZON.", "");
const builtInIntentSamples = lodash_1.default.get(DialogflowDefault_1.BUILT_IN_INTENTS, name, []);
samples = lodash_1.default(samples)
.concat(builtInIntentSamples)
.uniq()
.value();
events = name === "LaunchIntent" ? ["WELCOME", "GOOGLE_ASSISTANT_WELCOME"] : events;
events = events.map((eventName) => ({ name: eventName }));
const parameters = lodash_1.default(slotsDefinition)
.filter(slot => this.filterByPlatform(slot))
.map(slot => ({
dataType: lodash_1.default.includes(slot.type, "@sys.") ? slot.type : `@${slot.type}`,
name: slot.name,
value: `$${slot.name}`,
isList: false,
required: slot.required
}))
.value();
const resultSamples = samples.map(sample => {
const data = lodash_1.default.chain(sample)
.replace(/{([^}]+)}/g, (match, inner) => {
return `|{${inner}}|`;
})
.split("|")
.map(text => {
const element = {};
const isTemplate = lodash_1.default.includes(text, "{") && lodash_1.default.includes(text, "}");
const alias = text.replace("{", "").replace("}", "");
const slot = lodash_1.default.find(parameters, { name: text });
if (isTemplate && slot) {
const slotMeta = slot.dataType.includes("@sys.")
? slot.dataType
: `@${lodash_1.default.kebabCase(slot.dataType)}`;
lodash_1.default.set(element, "meta", slotMeta);
lodash_1.default.set(element, "alias", alias);
}
if (!lodash_1.default.isEmpty(text)) {
lodash_1.default.set(element, "text", text);
lodash_1.default.set(element, "userDefined", isTemplate);
}
lodash_1.default.set(element, "id", hashObj(element));
return lodash_1.default.isEmpty(lodash_1.default.omit(element, ["id"])) ? null : element;
})
.compact()
.value();
return {
data,
isTemplate: false,
count: 0,
updated: 0
};
});
if (!lodash_1.default.isEmpty(resultSamples)) {
const file = {
path: this.buildFilePath(environment, "intents", `${name}_usersays_${locale}.json`),
content: resultSamples
};
this.fileContent.push(file);
}
});
}
buildIntent(locale, environment) {
const intentsByPlatformAndEnvironments = this.intentsByPlatformAndEnvironments(locale, environment);
locale = this.getLocale(locale);
this.builtIntents = intentsByPlatformAndEnvironments.map((rawIntent) => {
let { name, events } = rawIntent;
const { parameterName, parameterValue } = rawIntent;
const { webhookForSlotFilling, slotsDefinition, responses, webhookUsed } = rawIntent;
name = name.replace("AMAZON.", "");
const fallbackIntent = name === "FallbackIntent";
const action = fallbackIntent ? "input.unknown" : name;
events = name === "LaunchIntent" ? ["WELCOME", "GOOGLE_ASSISTANT_WELCOME"] : events;
events = events.map((eventName) => ({ name: eventName }));
// tslint:disable-next-line: prefer-const
let parameters = lodash_1.default(slotsDefinition)
.filter(slot => this.filterByPlatform(slot))
.map(slot => ({
dataType: lodash_1.default.includes(slot.type, "@sys.") ? slot.type : `@${lodash_1.default.kebabCase(slot.type)}`,
name: slot.name.replace("{", "").replace("}", ""),
value: `$${slot.name.replace("{", "").replace("}", "")}`,
isList: false,
required: slot.required
}))
.value();
if (parameterName && parameterValue) {
parameters.push({
dataType: "",
name: parameterName,
value: parameterValue,
isList: false,
required: false
});
}
const messages = [];
if (responses.length > 0) {
messages.push({
type: 0,
lang: locale,
speech: responses
});
}
const intent = {
name,
auto: true,
contexts: [],
responses: [
{
resetContexts: false,
action,
affectedContexts: [],
parameters,
messages,
defaultResponsePlatforms: {},
speech: []
}
],
priority: 500000,
webhookUsed,
webhookForSlotFilling,
fallbackIntent,
events
};
lodash_1.default.set(intent, "id", hashObj(intent));
const file = {
path: this.buildFilePath(environment, "intents", `${intent.name}.json`),
content: intent
};
this.fileContent.push(file);
return intent;
});
}
buildEntities(locale, environment) {
const localeEntity = this.getLocale(locale);
this.getSlotsByIntentsDefinition(locale, environment)
.filter(slot => !lodash_1.default.includes(slot.name, "@sys."))
.forEach(rawSlot => {
const { name, values } = rawSlot;
const slotName = lodash_1.default.kebabCase(name);
const slotContent = {
name: slotName,
isOverridable: true,
isEnum: false,
automatedExpansion: false
};
lodash_1.default.set(slotContent, "id", hashObj(slotContent));
const fileDef = {
path: this.buildFilePath(environment, "entities", `${slotName}.json`),
content: slotContent
};
const fileValue = {
path: this.buildFilePath(environment, "entities", `${slotName}_entries_${localeEntity}.json`),
content: values
};
this.fileContent.push(fileDef, fileValue);
});
}
buildFilePath(...names) {
return super.buildFilePath(this.interactionOptions.speechPath, this.NAMESPACE, ...names);
}
}
exports.DialogflowSchema = DialogflowSchema;
function hashObj(obj) {
return v5_1.default(JSON.stringify(obj), v5_1.default.DNS);
}
function findIntent(name, intents) {
const intentName = name.replace("AMAZON.", "");
return lodash_1.default.find(intents, i => i.name === name || i.name === intentName);
}
//# sourceMappingURL=DialogflowSchema.js.map