UNPKG

voxa-dashbot

Version:

Integrate Dashbot analytics into your Alexa apps using the voxa framework

284 lines (246 loc) 7.67 kB
/* * 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. */ import DashbotAnalytics from "dashbot"; import _ from "lodash"; import { IVoxaEvent, IVoxaReply, VoxaApp, ITransition, GoogleAssistantEvent, } from "voxa"; import rp from "request-promise"; import { IDashbotRevenueEvent, IDashbotReferralEvent, IDashbotShareEvent, IDashbotPageLaunchEvent, IDashbotCustomEvent, } from "./events"; interface IOutgoingIntent { name?: string; input: IOutgoingInput[]; } interface IOutgoingInput { name: string; value: string; } const defaultConfig = { ignoreUsers: [], }; const dashbotIntegrations: any = { alexa: "alexa", botframework: "generic", dialogflow: "google", // DEPRECATED google: "google", }; export interface IVoxaDashbotConfig { alexa?: string; api_key?: string; botframework?: string; debug?: boolean; dialogflow?: string; printErrors?: boolean; redact?: boolean; suppressSending?: boolean; timeout?: number; } export function register(voxaApp: VoxaApp, config: IVoxaDashbotConfig) { const pluginConfig = _.merge({}, defaultConfig, config); const dashbotConfig = { debug: pluginConfig.debug, printErrors: pluginConfig.printErrors, redact: pluginConfig.redact, timeout: pluginConfig.timeout, }; voxaApp.onRequestStarted(initDashbot); voxaApp.onRequestStarted(trackIncoming); voxaApp.onBeforeReplySent(async (voxaEvent, reply, transition) => { let input; if (voxaEvent.dashbot) { input = voxaEvent.dashbot.input; } await trackOutgoing(voxaEvent, reply, transition, input); }); const alexaRequestTypes = [ "AudioPlayer.PlaybackStarted", "AudioPlayer.PlaybackFinished", "AudioPlayer.PlaybackNearlyFinished", "AudioPlayer.PlaybackStopped", "AudioPlayer.PlaybackFailed", "System.ExceptionEncountered", "PlaybackController.NextCommandIssued", "PlaybackController.PauseCommandIssued", "PlaybackController.PlayCommandIssued", "PlaybackController.PreviousCommandIssued", "AlexaSkillEvent.ProactiveSubscriptionChanged", "AlexaSkillEvent.SkillAccountLinked", "AlexaSkillEvent.SkillEnabled", "AlexaSkillEvent.SkillDisabled", "AlexaSkillEvent.SkillPermissionAccepted", "AlexaSkillEvent.SkillPermissionChanged", "AlexaHouseholdListEvent.ItemsCreated", "AlexaHouseholdListEvent.ItemsUpdated", "AlexaHouseholdListEvent.ItemsDeleted", "Connections.Response", "Display.ElementSelected", "CanFulfillIntentRequest", "GameEngine.InputHandlerEvent", "Alexa.Presentation.APL.UserEvent", "Alexa.Presentation.APLT.UserEvent", "Messaging.MessageReceived", ]; for (const requestType of alexaRequestTypes) { if (_.has(voxaApp, `on${requestType}`)) { voxaApp[`on${requestType}`](initDashbot); voxaApp[`on${requestType}`](trackIncoming); voxaApp[`on${requestType}`](trackOutgoing); } } function initDashbot(voxaEvent: IVoxaEvent) { if (!shouldTrack(voxaEvent)) { return; } const { platform } = voxaEvent; const apiKey = _.get(pluginConfig, platform.name) || pluginConfig.api_key; let outgoingIntent: any = {}; voxaEvent.dashbot = { promises: [], trackEvent: async function ( dashbotEvent: | IDashbotRevenueEvent | IDashbotReferralEvent | IDashbotShareEvent | IDashbotPageLaunchEvent | IDashbotCustomEvent ) { const requestBody = { ...dashbotEvent, ...{ userId: voxaEvent.user.userId, conversationId: voxaEvent.session.sessionId, }, }; voxaEvent.dashbot!.promises.push( rp.post({ uri: "https://tracker.dashbot.io/track", qs: { platform: dashbotIntegrations[platform.name], v: "11.1.0-rest", type: "event", apiKey: apiKey, }, json: true, body: requestBody, }) ); }, addInputs: function (outgoingInputs) { const input = _.get(outgoingInputs, "input"); outgoingIntent.input = outgoingInputs; if (input) { outgoingIntent.input = input; } }, get input() { return outgoingIntent.input; }, }; } function trackIncoming(voxaEvent: IVoxaEvent) { if (!shouldTrack(voxaEvent)) { return; } const { rawEvent, platform } = voxaEvent; const apiKey = _.get(pluginConfig, platform.name) || pluginConfig.api_key; const Dashbot = DashbotAnalytics(apiKey, dashbotConfig)[ dashbotIntegrations[platform.name] ]; voxaEvent.dashbot!.promises.push(Dashbot.logIncoming(rawEvent)); } async function trackOutgoing( voxaEvent: IVoxaEvent, reply: IVoxaReply, transition?: ITransition, input?: any ) { if (!shouldTrack(voxaEvent)) { return; } const { rawEvent, platform } = voxaEvent; const apiKey = _.get(pluginConfig, platform.name) || pluginConfig.api_key; const Dashbot = DashbotAnalytics(apiKey, dashbotConfig)[ dashbotIntegrations[platform.name] ]; if (transition && transition.say) { let says = transition.say; if (!_.isArray(transition.say)) { says = [transition.say]; } if (_.isPlainObject(input)) { input = [input]; } const intent = says.join(","); reply = { ...reply, ...{ intent: { name: intent, input, }, }, }; } if (isGoogleAssistant(voxaEvent)) { reply = _.merge({}, reply, { payload: { google: { userStorage: JSON.stringify({ ...JSON.parse(voxaEvent.dialogflow.conv.user._serialize()), dashbotUser: { userId: voxaEvent.user.userId, }, }), }, }, }); } voxaEvent.dashbot!.promises.push(Dashbot.logOutgoing(rawEvent, reply)); return Promise.all(voxaEvent.dashbot!.promises); } function shouldTrack(voxaEvent: IVoxaEvent): boolean { for (const ignoreRule of pluginConfig.ignoreUsers) { if (voxaEvent.user.userId.match(ignoreRule)) { return false; } } if (pluginConfig.suppressSending) { return false; } return true; } } function isGoogleAssistant( voxaEvent: IVoxaEvent ): voxaEvent is GoogleAssistantEvent { return voxaEvent.platform.name === "google"; }