Skip to content

Instantly share code, notes, and snippets.

@savelee
Created April 12, 2021 11:06
Show Gist options
  • Save savelee/89be48e8c603e4709a67521efb47611e to your computer and use it in GitHub Desktop.
Save savelee/89be48e8c603e4709a67521efb47611e to your computer and use it in GitHub Desktop.
google assistant - for CX agents
{
"actions": [
{
"description": "Default Welcome Intent",
"name": "MAIN",
"fulfillment": {
"conversationName": "welcome"
},
"intent": {
"name": "actions.intent.MAIN",
"trigger": {
"queryPatterns":["talk to CCAIDemo"]
}
}
},
{
"description": "Dialogflow Intents",
"name": "TEXT",
"fulfillment": {
"conversationName": "dialogflow_intent"
},
"intent": {
"name": "actions.intent.TEXT",
"trigger": {
"queryPatterns": []
}
}
}
],
"conversations": {
"welcome": {
"name": "welcome",
"url": "https://5d9233546d75.ngrok.io/aog/"
},
"dialogflow_intent": {
"name": "dialogflow_intent",
"url": "https://5d9233546d75.ngrok.io/aog/"
}
},
"locale": "en"
}
/**
* @license
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/
import * as actionsSdk from 'actions-on-google';
import { dialogflowv2beta1 } from './dialogflow-v2beta1';
import { dialogflowcx } from './dialogflow-cx';
import { dialogflowcxv3beta1 } from './dialogflow-cxv3beta1';
import { pubsub } from './pubsub';
import { debug } from './debug';
import * as dotenv from 'dotenv';
dotenv.config();
const df = process.env.DF || 'v2beta1';
export class Aog {
private assistant: any;
private dialogflow: any;
constructor() {
this.assistant = actionsSdk.actionssdk();
if(df === 'cx') {
this.dialogflow = dialogflowcx;
} else if(df === 'cxv3beta1') {
this.dialogflow = dialogflowcxv3beta1;
} else {
this.dialogflow = dialogflowv2beta1;
}
}
/**
* Register handlers for Actions SDK intents
*/
public registerHandlers(expressApp): void{
this.assistant.intent('actions.intent.MAIN', async (conv) => {
return this.detectIntentEvent(conv, 'WELCOME');
});
this.assistant.intent('actions.intent.TEXT', async (conv, input) => {
if (input === 'bye' || input === 'goodbye') {
// TODO get this from the Dialogflow end
return conv.close('See you later!');
}
// Pass everything to Dialogflow
debug.log(input);
return this.detectIntentText(conv, input)
});
expressApp.post('/aog/', this.assistant)
}
createRichMessages(conv, response){
const msg = response.responseMessages[0].text.text[0];
conv.ask(msg);
}
async detectIntentEvent(conv: any, eventName: string){
const aogResponse = await this.dialogflow.detectIntentEvent(eventName);
aogResponse.platform = 'googleassistant';
console.log(aogResponse);
pubsub.pushToChannel(aogResponse);
return this.createRichMessages(conv, aogResponse);
}
async detectIntentText(conv: any, query: string) {
const aogResponse = await this.dialogflow.detectIntentText(query);
aogResponse.platform = 'googleassistant';
pubsub.pushToChannel(aogResponse);
return this.createRichMessages(conv, aogResponse);
}
}
export let aog = new Aog();
/**
* @license
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/
import * as df from '@google-cloud/dialogflow-cx';
import * as dotenv from 'dotenv';
import * as uuid from 'uuid';
import { debug } from './debug';
import { BotResponse } from './dialogflow-bot-responses';
const struct = require('./structjson');
dotenv.config();
const langCode = process.env.LANGUAGE_CODE || 'en-US';
const encoding = process.env.ENCODING || 'AUDIO_ENCODING_LINEAR_16';
const sampleRateHertz = parseInt(process.env.SAMPLE_RATE_HERTZ, 10) || 16000;
const location = process.env.LOCATION || 'global';
const agentId = process.env.AGENT_ID;
const projectId = process.env.GCP_PROJECT;
export interface QueryInputCX {
text?: {
text: string,
}
intent?: {
intent: string // projects/<Project ID>/locations/<Location ID>/agents/<Agent ID>/intents/<Intent ID>.
},
event?: {
event: string
},
audio?: {
config: {
audioEncoding: any,
sampleRateHertz: number
},
audio?: any
},
dtmf?: {
digits: string,
finish_digit: string
},
languageCode: string
}
export class DialogflowCX {
protected sessionClient: df.SessionsClient | df.v3beta1.SessionsClient;
private projectId: string;
private agentId: string;
private location: string;
private sessionId: string;
private sessionPath: string;
constructor() {
this.projectId = projectId;
this.agentId = agentId;
this.location = location;
this.sessionId = uuid.v4();
this.sessionClient = new df.SessionsClient(
{ apiEndpoint: `${this.location}-dialogflow.googleapis.com` }
);
this.sessionPath = this.sessionClient.projectLocationAgentSessionPath(
this.projectId,
this.location,
this.agentId,
this.sessionId
);
}
detectIntentText(query: string, lang = langCode, contexts?: Array<string>) {
const qInput:QueryInputCX = {
text: {
text: query,
},
languageCode: lang
};
return this.detectIntent(qInput, query, contexts);
}
detectIntentEvent(eventName: string, lang = langCode) {
const qInput:QueryInputCX = {
event: {
event: eventName,
},
languageCode: lang
};
return this.detectIntent(qInput, eventName);
}
detectIntentAudioStream(stream, lang = langCode){
const qInput:QueryInputCX = {
audio: {
config: {
audioEncoding: encoding,
sampleRateHertz,
}
},
languageCode: lang
};
return this.detectIntent(qInput, 'audio');
}
async detectIntent(qInput:QueryInputCX, input?: string, contexts?: Array<string>) {
const request = {
session: this.sessionPath,
queryInput: qInput,
queryParams: null
};
if (contexts && contexts.length > 0) {
request.queryParams.contexts = contexts;
}
var botResponse;
try {
const [response] = await this.sessionClient.detectIntent(request);
debug.log(response);
botResponse = this.beautifyResponses(response, input);
} catch(e) {
debug.error(e);
botResponse = this.beautifyResponses(null, input, e);
}
debug.log(botResponse);
return botResponse;
}
beautifyResponses(response: any, input: string, e?: any): BotResponse{
var botResponse: BotResponse;
const dialogflowConfig = {
sessionId: this.sessionId,
sessionPath: this.sessionPath,
projectId: this.projectId,
agentId: this.agentId,
location: this.location,
dateTimeStamp: new Date().getTime()/1000,
text: input, // in case DF doesn't respond anything, we can still capture these
languageCode: langCode, // in case DF doesn't respond anything, we can still capture these
}
if(e) {
debug.error(e);
dialogflowConfig['error'] = e.message;
}
if(response && response.queryResult){
var dialogflowResponses = {
languageCode: response.queryResult.languageCode, // override
sentiment: response.queryResult.sentimentAnalysisResult,
currentPage: response.queryResult.currentPage,
query: response.queryResult.query,
text: response.queryResult.text, // override
responseMessages: response.queryResult.responseMessages,
webhookPayloads: response.queryResult.webhookPayloads,
webhookStatuses: response.queryResult.webhookStatuses,
outputAudio: response.outputAudio,
responseId: response.responseId
}
if(response.queryResult.intent){
const intentDetectionObj = {
intentDetection: {
intent: {
displayName: response.queryResult.intent.displayName,
name: response.queryResult.intent.name,
parameters: response.queryResult.intent.parameters,
priority: response.queryResult.intent.priority,
trainingPhrases: response.queryResult.intent.trainingPhrases,
isFallback: response.queryResult.intent.isFallback,
intentDetectionConfidence: response.queryResult.intentDetectionConfidence
}
}
};
dialogflowResponses = {...dialogflowResponses, ...intentDetectionObj }
}
botResponse = {...dialogflowConfig, ...dialogflowResponses }
} else {
botResponse = dialogflowConfig;
}
return botResponse;
}
}
export let dialogflowcx = new DialogflowCX();
/**
* @license
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/
import * as http from 'http';
import { Server } from 'socket.io';
import * as express from 'express';
import * as cors from 'cors';
import { aog } from './aog';
import { debug } from './debug';
import * as dotenv from 'dotenv';
dotenv.config();
const projectId = process.env.GCP_PROJECT;
const port = process.env.PORT;
export class App {
public static readonly PORT:number = parseInt(port, 10);
private app: express.Application;
private server: http.Server;
private io: Server;
constructor() {
this.createApp();
this.createServer();
this.sockets();
this.listen();
}
private createApp(): void {
this.app = express();
this.app.use(cors());
this.app.use(express.urlencoded({
extended: true
}));
this.app.use(express.json());
this.app.use(function(req: any, res: any, next: any) {
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
next();
});
this.app.get('/', function(req, res) {
res.json({success: 'true'});
});
aog.registerHandlers(this.app);
}
private createServer(): void {
this.server = http.createServer(this.app);
}
private listen(): void {
this.server.listen(App.PORT, () => {
debug.log('Running chat server on port ' + App.PORT);
debug.log('Project ID: ' + projectId);
});
}
public getApp(): express.Application {
return this.app;
}
}
export let app = new App();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment