@botmock/export
Provides classes and methods for integration of Botmock data with development platforms.
Stock exporters
Install
npm install @botmock/export
Stock exporters example
Note: migrating from standalone exporters
The previous exporters were designed to transform your Botmock project directly into the format required by the end platform by cloning a particular repository, running npm install
and then npm start
. @botmock/export
, however, has been redesigned to be more granular and developer-friendly. Let's see what this means.
This short example shows how to use the included DialogflowExporter
to export data from a specific Botmock project to the Dialogflow platform.
import { DialogflowExporter, FileWriter, Kind } from "@botmock/export";
const projectReference: ProjectReference = {
teamId: process.env.TEAM_ID as string,
projectId: process.env.PROJECT_ID as string,
boardId: process.env.BOARD_ID,
};
async function main(): Promise<void> {
const exporter = new DialogflowExporter({ token: process.env.TOKEN as string });
const { data } = await exporter.exportProjectUnderDataTransformations({ projectReference });
const writeResult = await (new FileWriter()).writeAllResourcesToFiles({ data });
if (writeResult.kind === Kind.OK) {
console.log(writeResult.value);
}
}
main().catch((err: Error) => {
console.error(err);
});
Custom exporters example
What if you are targeting a platform that is not included in the stock exporters?
BaseExporter
is available on @botmock/export
for this reason.
Let's see how to use it.
Create a new file "./exporter.ts", and add the following.
import { BaseExporter } from "@botmock/export";
Imagine we have our own platform called "Flatland", and imagine this platform describes projects via a .yml
file and .json
file.
We would like to integrate data from Botmock into it.
The first step is to write our own class that extends BaseExporter
.
import {
BaseExporter,
DataTransformation,
Resources,
Botmock,
} from "@botmock/export";
export class FlatlandExporter extends BaseExporter { }
This code produces a TypeScript compilation error. This is because BaseExporter
needs to have a property called dataTransformations
defined on it.
dataTransformations
is a Map
that relates directory paths to functions that take Resources
,
and return DataTransformation
.
Resources
resources
are the single parameter passed to every value in a dataTranformation
Map
.
Resources might look like the following.
{
"intents": [
{
"id": "6ef70cd0-9c20-11ea-9d3b-91ab6dd1aa2f",
"name": "begin",
"utterances": {
"en": [
{
"text": "hi",
"variables": []
}
]
},
"created_at": {
"date": "2020-05-22 11:36:10.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"updated_at": {
"date": "2020-05-22 11:36:10.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"is_global": false,
"slots": []
},
{
"id": "2ff08a20-8e17-11ea-9af2-3b66755d0983",
"name": "one",
"utterances": {
"en": [
{
"text": "one",
"variables": []
}
]
},
"created_at": {
"date": "2020-05-04 14:54:42.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"updated_at": {
"date": "2020-05-04 14:54:42.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"is_global": false,
"slots": []
},
{
"id": "0c900ab0-a5ac-11ea-b102-696fc338cf15",
"name": "second_intent",
"utterances": {
"en": [
{
"text": "give %ReservationPersonsNumber%",
"variables": [
{
"id": "fe1c9ed0-8e0e-11ea-a54c-35dc7cb8db58",
"name": "%ReservationPersonsNumber%",
"type": "int",
"entity": "d9e693a0-4455-11ea-b208-ab55d7896de3",
"default_value": "",
"start_index": 5
}
]
}
]
},
"created_at": {
"date": "2020-06-03 15:08:15.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"updated_at": {
"date": "2020-06-03 15:08:15.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"is_global": false,
"slots": [
{
"id": "f0256a7b-600a-45a1-a818-9fadadce0ae4",
"variable_id": "fe1c9ed0-8e0e-11ea-a54c-35dc7cb8db58",
"is_required": true,
"prompt": "missing persons!"
}
]
}
],
"entities": [
{
"id": "fe1a7800-8e0e-11ea-b472-e3ecdf3932f8",
"name": "DinnerOrLunch",
"data": {
"en": [
{
"value": "dinner",
"synonyms": []
},
{
"value": "lunch",
"synonyms": []
}
]
},
"created_at": {
"date": "2020-05-04 13:56:03.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"updated_at": {
"date": "2020-05-04 13:56:03.000000",
"timezone_type": 3,
"timezone": "UTC"
}
},
{
"id": "fe1e56d0-8e0e-11ea-8a1a-45ca961859cb",
"name": "ReservationPersonsNumber",
"data": {
"en": [
{
"value": "42",
"synonyms": []
}
]
},
"created_at": {
"date": "2020-05-04 13:56:03.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"updated_at": {
"date": "2020-05-04 13:56:03.000000",
"timezone_type": 3,
"timezone": "UTC"
}
}
],
"variables": [
{
"id": "fe17b400-8e0e-11ea-89b9-93f612e81ac2",
"name": "DinnerOrLunch",
"default_value": "dinner",
"type": "text",
"entity": "f62f1e60-4455-11ea-ae09-b7ba8a9ae270",
"created_at": {
"date": "2020-05-04 13:56:03.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"updated_at": {
"date": "2020-05-04 13:56:03.000000",
"timezone_type": 3,
"timezone": "UTC"
}
},
{
"id": "fe1c9ed0-8e0e-11ea-a54c-35dc7cb8db58",
"name": "ReservationPersonsNumber",
"default_value": "",
"type": "int",
"entity": "d9e693a0-4455-11ea-b208-ab55d7896de3",
"created_at": {
"date": "2020-05-04 13:56:03.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"updated_at": {
"date": "2020-05-04 13:56:03.000000",
"timezone_type": 3,
"timezone": "UTC"
}
}
],
"project": {
"id": "8d550c20-8d3a-11ea-aa4e-5f57749d4d32",
"name": "basic",
"type": "flow",
"platform": "multi-channel",
"channels": [
"google-actions",
"generic"
],
"locales": [
"en",
"es"
],
"created_at": {
"date": "2020-05-03 12:35:20.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"updated_at": {
"date": "2020-06-17 13:28:26.000000",
"timezone_type": 3,
"timezone": "UTC"
}
},
"board": {
"board": {
"root_messages": [
"8d585900-8d3a-11ea-b32a-67229b391039"
],
"messages": [
{
"message_id": "8d585900-8d3a-11ea-b32a-67229b391039",
"message_type": "response",
"next_message_ids": [
{
"message_id": "0ac39c3f-ebfe-4f31-8ec9-a551395e4d6b",
"action": "*",
"intent": {
"label": "begin",
"value": "6ef70cd0-9c20-11ea-9d3b-91ab6dd1aa2f"
},
"conditional": {
"id": "g-V7kpFszuWdt8q_wdDnXor",
"rules": [],
"combinator": "and",
"not": false
}
}
],
"previous_message_ids": [],
"is_root": true,
"node_name": "Starting Point",
"payload": {
"en": {
"generic": {
"blocks": []
}
}
}
},
{
"message_id": "0ac39c3f-ebfe-4f31-8ec9-a551395e4d6b",
"message_type": "response",
"next_message_ids": [
{
"message_id": "52611df2-7216-4c0c-94bf-3707b08eda4b",
"action": "*",
"intent": {
"label": "one",
"value": "2ff08a20-8e17-11ea-9af2-3b66755d0983"
},
"conditional": {
"id": "g-CSNPKr7MiwLxfeUMUdb3A",
"rules": [],
"combinator": "and",
"not": false
}
}
],
"previous_message_ids": [
{
"message_id": "8d585900-8d3a-11ea-b32a-67229b391039",
"action": "*"
}
],
"is_root": false,
"node_name": "First block",
"payload": {
"en": {
"generic": {
"blocks": [
{
"component_type": "text",
"nodeName": "Bot Says",
"context": [],
"text": "one thing",
"audio_file": "",
"ssml": "",
"delay": 2000,
"alternate_replies": [
"another thing"
]
},
{
"component_type": "text",
"nodeName": "Bot Says",
"context": [],
"text": "one more thing!",
"audio_file": "",
"ssml": "",
"delay": 2000
},
{
"component_type": "button",
"nodeName": "Button",
"context": [],
"text": "one",
"buttons": [
{
"type": "postback",
"title": "one",
"payload": "ONE"
}
],
"audio_file": "",
"ssml": "",
"delay": 2000,
"nextMessagesIds": []
}
]
}
}
}
},
{
"message_id": "52611df2-7216-4c0c-94bf-3707b08eda4b",
"message_type": "response",
"next_message_ids": [
{
"message_id": "7e236f12-d02a-4eac-8939-1221533baa4c",
"action": "*",
"intent": {
"label": "second_intent",
"value": "0c900ab0-a5ac-11ea-b102-696fc338cf15"
},
"conditional": {
"id": "g-P6xjtHxlyKJ6wHjN8FLYw",
"rules": [],
"combinator": "and",
"not": false
}
}
],
"previous_message_ids": [
{
"message_id": "0ac39c3f-ebfe-4f31-8ec9-a551395e4d6b",
"action": "*"
}
],
"is_root": false,
"node_name": "Response block",
"payload": {
"en": {
"generic": {
"blocks": [
{
"component_type": "text",
"nodeName": "Bot Says",
"context": [],
"text": "Says!",
"audio_file": "",
"ssml": "",
"delay": 2000
}
]
},
"google-actions": {
"blocks": [
{
"component_type": "text",
"nodeName": "Bot Says",
"text": "Your text here",
"buttons": [],
"ssml": "",
"audio_file": "",
"delay": 2000
},
{
"component_type": "card",
"nodeName": "Card",
"image_url": "https://app.botmock.com/img/placeholder.png",
"title": "This is your card title",
"subtitle": "",
"text": "This is the body of the card.",
"link": "",
"ssml": "",
"audio_file": "",
"delay": 2000
}
]
}
}
}
},
{
"message_id": "7e236f12-d02a-4eac-8939-1221533baa4c",
"message_type": "response",
"next_message_ids": [],
"previous_message_ids": [
{
"message_id": "52611df2-7216-4c0c-94bf-3707b08eda4b",
"action": "*"
}
],
"is_root": false,
"node_name": "Response block",
"payload": {
"en": {
"google-actions": {
"blocks": []
},
"generic": {
"blocks": [
{
"component_type": "quick_replies",
"nodeName": "Quick Replies",
"text": "Your text goes here...",
"quick_replies": [
{
"content_type": "text",
"title": "Button",
"image_url": "",
"payload": "BUTTON"
}
],
"audio_file": "",
"ssml": "",
"delay": 2000
}
]
}
},
"es": {
"google-actions": {
"blocks": []
},
"generic": {
"blocks": []
}
}
}
}
]
},
"slots": {
"6ef70cd0-9c20-11ea-9d3b-91ab6dd1aa2f": [],
"2ff08a20-8e17-11ea-9af2-3b66755d0983": [],
"0c900ab0-a5ac-11ea-b102-696fc338cf15": [
{
"id": "f0256a7b-600a-45a1-a818-9fadadce0ae4",
"variable_id": "fe1c9ed0-8e0e-11ea-a54c-35dc7cb8db58",
"is_required": true,
"prompt": "missing persons!"
}
]
},
"variables": [
{
"id": "fe17b400-8e0e-11ea-89b9-93f612e81ac2",
"name": "DinnerOrLunch",
"default_value": "dinner",
"type": "text",
"entity": "f62f1e60-4455-11ea-ae09-b7ba8a9ae270",
"created_at": {
"date": "2020-05-04 13:56:03.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"updated_at": {
"date": "2020-05-04 13:56:03.000000",
"timezone_type": 3,
"timezone": "UTC"
}
},
{
"id": "fe1c9ed0-8e0e-11ea-a54c-35dc7cb8db58",
"name": "ReservationPersonsNumber",
"default_value": "",
"type": "int",
"entity": "d9e693a0-4455-11ea-b208-ab55d7896de3",
"created_at": {
"date": "2020-05-04 13:56:03.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"updated_at": {
"date": "2020-05-04 13:56:03.000000",
"timezone_type": 3,
"timezone": "UTC"
}
}
],
"created_at": {
"date": "2020-05-03 12:35:20.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"updated_at": {
"date": "2020-06-17 13:28:11.000000",
"timezone_type": 3,
"timezone": "UTC"
}
}
}
DataTransformation
DataTransformation
interface should be returned by every function in a dataTranformation
Map
.
It can be an array of objects, or a single object that has the following structure
{
filename: string;
data: any;
}
export class FlatlandExporter extends BaseExporter {
#outputDataTransformation = (resources: Resources): DataTransformation => {
return [
{
filename: `${resources.project.name}.geo.yml`,
data: this.#createGeometries({
board: resources.board,
project: resources.project,
intents: resources.intents,
entities: resources.entities,
variables: resources.variables,
}),
},
{
filename: `${resources.project.name}.ctx.json`,
data: this.#createConversationContext(),
},
];
};
dataTransformations = new Map([
["./output", this.#outputDataTransformation],
]);
}
Then, FlatlandExporter
can be used to write files just like in the stock exporter example.
async function main(args: string[]): Promise<void> {
const exporter = new FlatlandExporter({ token: process.env.TOKEN as string });
const { data } = await exporter.exportProjectUnderDataTransformations({ projectReference });
await (new FileWriter()).writeAllResourcesToFiles({ data });
}
data
structure
The data
property returned by exportProjectUnderDataTransformations
is an object that relates directory paths to objects describing the filename and contents.
It may look like the following.
{
"./": [
{
"filename": "agent.json",
"data": {
"description": "",
"language": "en",
"shortDescription": "",
"examples": "",
"linkToDocs": "",
"disableInteractionLogs": false,
"disableStackdriverLogs": true,
"googleAssistant": {
"googleAssistantCompatible": false,
"project": "basic",
"welcomeIntentSignInRequired": false,
"startIntents": [],
"systemIntents": [],
"endIntentIds": [],
"oAuthLinking": {
"required": false,
"providerId": "",
"authorizationUrl": "",
"tokenUrl": "",
"scopes": "",
"privacyPolicyUrl": "",
"grantType": "AUTH_CODE_GRANT"
},
"voiceType": "MALE_1",
"capabilities": [],
"env": "",
"protocolVersion": "V2",
"autoPreviewEnabled": false,
"isDeviceAgent": false
},
"defaultTimezone": "America/Barbados",
"webhook": {
"url": "",
"username": "",
"headers": {},
"available": false,
"useForDomains": false,
"cloudFunctionsEnabled": false,
"cloudFunctionsInitialized": false
},
"isPrivate": true,
"customClassifierMode": "use.after",
"mlMinConfidence": 0.3,
"supportedLanguages": [
"es"
],
"onePlatformApiVersion": "v2",
"analyzeQueryTextSentiment": false,
"enabledKnowledgeBaseNames": [],
"knowledgeServiceConfidenceAdjustment": -0.4,
"dialogBuilderMode": false,
"baseActionPackagesUrl": ""
}
},
{
"filename": "package.json",
"data": {
"version": "1.0.0"
}
}
],
"./entities": [
{
"filenames": [
"DinnerOrLunch.json",
"DinnerOrLunch_entries_en.json"
],
"data": [
{
"id": "fe1a7800-8e0e-11ea-b472-e3ecdf3932f8",
"name": "DinnerOrLunch",
"isOverridable": true,
"isEnum": false,
"isRegexp": false,
"automatedExpansion": false,
"allowFuzzyExtraction": false
},
[
{
"value": "dinner",
"synonyms": []
},
{
"value": "lunch",
"synonyms": []
}
]
]
},
{
"filenames": [
"ReservationPersonsNumber.json",
"ReservationPersonsNumber_entries_en.json"
],
"data": [
{
"id": "fe1e56d0-8e0e-11ea-8a1a-45ca961859cb",
"name": "ReservationPersonsNumber",
"isOverridable": true,
"isEnum": false,
"isRegexp": false,
"automatedExpansion": false,
"allowFuzzyExtraction": false
},
[
{
"value": "42",
"synonyms": []
}
]
]
}
],
"./intents": [
{
"filenames": [
"begin.json",
"begin_usersays_en.json"
],
"data": [
{
"id": "0ac39c3f-ebfe-4f31-8ec9-a551395e4d6b",
"name": "begin",
"auto": true,
"contexts": [
"begin"
],
"responses": [
{
"resetContexts": false,
"action": "input.welcome",
"affectedContexts": [
{
"name": "welcome",
"parameters": {},
"lifespan": 5
},
{
"name": "one",
"parameters": {},
"lifespan": 5
}
],
"parameters": [],
"messages": [
{
"type": 0,
"lang": "en",
"condition": "",
"speech": "one thing"
},
{
"type": 0,
"lang": "en",
"condition": "",
"speech": "one more thing!"
},
{
"type": 4,
"lang": "en",
"condition": "",
"payload": {
"component_type": "button",
"nodeName": "Button",
"context": [],
"text": "one",
"buttons": [
{
"type": "postback",
"title": "one",
"payload": "ONE"
}
],
"audio_file": "",
"ssml": "",
"delay": 2000,
"nextMessagesIds": []
}
}
],
"defaultResponsePlatforms": {},
"speech": []
}
],
"priority": 5000,
"webhookUsed": false,
"webhookForSlotFilling": false,
"fallbackIntent": false,
"events": [
{
"name": "WELCOME"
}
],
"conditionalResponses": [],
"condition": "",
"conditionalFollowupEvents": []
},
[
{
"id": "ed59bc6f-b208-42a8-bbaa-253747941ee1",
"data": [
{
"text": "hi",
"userDefined": false
}
],
"isTemplate": false,
"count": 0,
"updated": 0
},
{
"id": "f22e3a1d-c811-4838-8c54-74e7539c0cbb",
"data": [
{
"text": "hi",
"userDefined": false
}
],
"isTemplate": false,
"count": 0,
"updated": 0,
"isAuto": false
}
]
]
},
{
"filenames": [
"one.json",
"one_usersays_en.json"
],
"data": [
{
"id": "52611df2-7216-4c0c-94bf-3707b08eda4b",
"name": "one",
"auto": true,
"contexts": [
"one",
"begin"
],
"responses": [
{
"resetContexts": false,
"action": "input.welcome",
"affectedContexts": [
{
"name": "second_intent",
"parameters": {},
"lifespan": 5
}
],
"parameters": [],
"messages": [
{
"type": 0,
"lang": "en",
"condition": "",
"speech": "Says!"
},
{
"type": "simple_response",
"lang": "en",
"condition": "",
"textToSpeech": "Your text here"
},
{
"type": 4,
"lang": "en",
"condition": "",
"payload": {
"component_type": "card",
"nodeName": "Card",
"image_url": "https://app.botmock.com/img/placeholder.png",
"title": "This is your card title",
"subtitle": "",
"text": "This is the body of the card.",
"link": "",
"ssml": "",
"audio_file": "",
"delay": 2000
}
}
],
"defaultResponsePlatforms": {},
"speech": []
}
],
"priority": 5000,
"webhookUsed": false,
"webhookForSlotFilling": false,
"fallbackIntent": false,
"events": [],
"conditionalResponses": [],
"condition": "",
"conditionalFollowupEvents": []
},
[
{
"id": "aa82f86e-82a0-42e9-b330-47868471c263",
"data": [
{
"text": "hi",
"userDefined": false
}
],
"isTemplate": false,
"count": 0,
"updated": 0
},
{
"id": "0be85546-26f3-4539-b490-bb12d3f5ece2",
"data": [
{
"text": "one",
"userDefined": false
}
],
"isTemplate": false,
"count": 0,
"updated": 0,
"isAuto": false
}
]
]
},
{
"filenames": [
"second_intent.json",
"second_intent_usersays_en.json"
],
"data": [
{
"id": "7e236f12-d02a-4eac-8939-1221533baa4c",
"name": "second_intent",
"auto": true,
"contexts": [
"second_intent",
"one",
"begin"
],
"responses": [
{
"resetContexts": false,
"action": "input.welcome",
"affectedContexts": [],
"parameters": [
{
"id": "fe1c9ed0-8e0e-11ea-a54c-35dc7cb8db58",
"required": false,
"name": "ReservationPersonsNumber",
"value": "$",
"promptMessages": [],
"noMatchPromptMessages": [],
"outputDialogContexts": [],
"isList": false
}
],
"messages": [
{
"type": 4,
"lang": "en",
"condition": "",
"payload": {
"component_type": "quick_replies",
"nodeName": "Quick Replies",
"text": "Your text goes here...",
"quick_replies": [
{
"content_type": "text",
"title": "Button",
"image_url": "",
"payload": "BUTTON"
}
],
"audio_file": "",
"ssml": "",
"delay": 2000
}
}
],
"defaultResponsePlatforms": {},
"speech": []
}
],
"priority": 5000,
"webhookUsed": false,
"webhookForSlotFilling": false,
"fallbackIntent": false,
"events": [],
"conditionalResponses": [],
"condition": "",
"conditionalFollowupEvents": []
},
[
{
"id": "cad355d2-20bb-44b8-9fa4-0531d1307ea5",
"data": [
{
"text": "hi",
"userDefined": false
}
],
"isTemplate": false,
"count": 0,
"updated": 0
},
{
"id": "baf89010-c0d5-4159-a162-651b7a31aac2",
"data": [
{
"text": "give $ReservationPersonsNumber",
"userDefined": false
}
],
"isTemplate": false,
"count": 0,
"updated": 0,
"isAuto": false
}
]
]
}
]
}
Supported file extensions
The following filetypes are supported for the filename
property within DataTransformation
.