I have an Express app that I am trying to integrate with Google Assistant.
I've installed https://www.npmjs.com/package/actions-on-google and have followed https://codelabs.developers.google.com/codelabs/actions-1/#0 which deploys functions to Firebase - however I would like to run them from a self-hosted Express server.
In my app.js I have set up as follows:
const {
dialogflow,
Image,
} = require('actions-on-google')
// Create an app instance
const gapp = dialogflow();
});
However I am unsure how to create the route that I add in Dialogflow console as the webhook - do I use the format below?
app.post('/webhook', function(req, res){
gapp.intent('favorite color', (conv, {color}) => {
const luckyNumber = color.length;
// Respond with the user's lucky number and end the conversation.
conv.close('Your lucky number is ' + luckyNumber);
});
});
If so do all of my intents then go within this route?
EDIT
Updated in response to answer:
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const {
dialogflow,
Image,
} = require('actions-on-google')
// Create a google app instance
const gapp = dialogflow()
// Register handlers for Dialogflow intents
gapp.intent('Default Welcome Intent', conv => {
conv.ask('Hi, how is it going?')
conv.ask(`Here's a picture of a cat`)
conv.ask(new Image({
url: 'https://developers.google.com/web/fundamentals/accessibility/semantics-builtin/imgs/160204193356-01-cat-500.jpg',
alt: 'A cat',
}))
})
// Intent in Dialogflow called `Goodbye`
gapp.intent('Goodbye', conv => {
conv.close('See you later!')
})
gapp.intent('Default Fallback Intent', conv => {
conv.ask(`I didn't understand. Can you tell me something else?`)
})
app.post('/ga/webhook', gapp)
You can define all of your gapp intents at the start of your Express server, then you can pass in your gapp object into the webhook that you define:
const express = require('express')
const bodyParser = require('body-parser')
// ... gapp code here
const expressApp = express().use(bodyParser.json())
expressApp.post('/webhook', gapp)
expressApp.listen(3000)
Related
I am trying to develop a chatbot using microsoft bot framework and I am also using botbuilder-adaptor-slack npm package to connect it to slack. This is following code which I have written in javascript:
const restify = require('restify');
const { BotFrameworkAdapter, UserState, MemoryStorage } = require('botbuilder');
const {SlackAdapter} = require('botbuilder-adapter-slack')
const WelcomeBot = require('./functions/welcomebot')
const memoryStorage = new MemoryStorage();
const userState = new UserState(memoryStorage);
const bot = new WelcomeBot.WelcomeBot(userState);
const adapter = new SlackAdapter({clientSigningSecret:process.env.CLIENT_SECRET,
verificationToken:process.env.verificationToken,
botToken:process.env.SLACK_TOKEN,
oauthVersion: 'v2'
});
adapter.onTurnError = async(context,error) => {
}
let server = restify.createServer()
server.use(restify.plugins.bodyParser());
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async(context) => {
console.log(req.body)
await bot.run(context);
})
})
server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log(`\n${server.name} listening to ${server.url}`);
});
I am getting the following error when I am trying to run the code.
Can somebody please help me with this code?
Looks like you're missing your auth information. You need to either:
Run in debug mode with a breakpoint on const adapter = new SlackAdapter... and ensure that process.env.<variable>, or
Add console.log(JSON.stringify(process.env, null, 2))
to ensure your environment variables (process.env.CLIENT_SECRET, process.env.verificationToken, and process.env.SLACK_TOKEN) are actually being set and exist.
If they are, then one of them is incorrect.
If they are not set or don't exist in process.env, you likely need to:
Create a .env file with:
CLIENT_SECRET=<yourClientSecret>
verificationToken=<yourVerificationToken>
SLACK_TOKEN=<yourSlackToken>
Note: Capitalization matters!
Add the following to your code, just after your import statements:
const dotenv = require('dotenv');
// Import required bot configuration.
const ENV_FILE = path.join(__dirname, '.env');
dotenv.config({ path: ENV_FILE });
I'd wager your issue is just that you're missing this code
I would like to use the "boilerplate"-structure (see github), use firebase for productive code, but ngrok during development. How can I combine it?
https://github.com/actions-on-google/dialogflow-webhook-boilerplate-nodejs/blob/master/functions/index.js
'use strict';
const {dialogflow} = require('actions-on-google');
const functions = require('firebase-functions');
const app = dialogflow({debug: true});
app.intent('Default Welcome Intent', (conv) => {
conv.close('Hello, World!');
// Complete your fulfillment logic and
// send a response when the function is done executing
});
exports.yourAction = functions.https.onRequest(app);
I have created simple Action-on google project and build Action with dialogflow.
In Dialogflow Intents are "welcome intent" -> follow-up-yes
And then fulfillment enabled (firebase http request) with local development project.
in my local project, I'm trying to call anotherfile(app.js) within index.js(The main file)
Plese tell me how to do that?
index.js
'use strict';
const functions = require('firebase-functions');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(express.json());
app.use('/', require('./routes/app'));
const helloWorld = functions.https.onRequest(app);
module.exports = {helloWorld};
routes/app.js
const express = require('express');
const router = express.Router();
const {dialogflow} = require('actions-on-google');
const app = dialogflow({debug: true});
app.intent('Welcome-Intent-yes', (conv, param) => {
conv.close('Plese login ');
});
router.use(app);
module.exports = router;
-------- edited info ------
This is my dialogflow agent with intent
output should be coming from my local code. which is
app.intent('Welcome-Intent-yes', (conv, param) => {
conv.close('Please login ');
});
i'm not getting any firebse deploy error, but i'm not getting above output from dialogflow.
But if i only use index.js file for my function, then it works and it gives an output from dialogflow
but my requirment is to call another file with my functions inside the index.js file, like what i have done on the above code.
I have a serverless app where I want to run my logic from the chatbot request coming from Facebook Messenger. When I run the intent function for test_handler I get the correct response back. But after I added another handler for skillRatio I seem to be getting the error in the title i.e
Error: Platform can NOT be empty at new Payload
. My code is as below.
const serverless = require('serverless-http');
const bodyParser = require('body-parser');
const express = require('express');
const app = express();
app.use(bodyParser.json({ strict: false }));
const {WebhookClient, Payload, Image, Card, Suggestion} = require('dialogflow-fulfillment');
const request = require('request');
app.get('/', function (req, res) {
res.send('Hello World !!!\n');
console.log("Testing express lambda\n");
})
app.post('/', function (req, res) {
const agent = new WebhookClient({request: req, response: res});
function test_handler(agent) {
agent.add(`Welcome to my agent on AWS Lambda!`);
agent.add(new Image("https://image-charts.com/chart?chs=700x190&chd=t:60,40&cht=p3&chl=Hello%7CWorld&chf=ps0-0,lg,45,ffeb3b,0.2,f44336,1|ps0-1,lg,45,8bc34a,0.2,009688,1"))
}
function skillRatio(agent) {
agent.add(`Let me just have a look and I'll gather the data. *Processing Chart Data....Mmmm Pie*.
Here we go! Here is the data on your $Skills.original request.`);
//agent.add(`Feel free to save or share :)`);
//agent.add(new Image("https://image-charts.com/chart?chs=700x190&chd=t:60,40&cht=p3&chl=Hello%7CWorld&chf=ps0-0,lg,45,ffeb3b,0.2,f44336,1|ps0-1,lg,45,8bc34a,0.2,009688,1"))
}
// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
intentMap.set('super-test', test_handler);
//intentMap.set('skill-ratio', skillRatio);
if (agent.requestSource === agent.FACEBOOK) {
intentMap.set('super-test', test_handler);
intentMap.set('skill-ratio', skillRatio);
} else {
}
agent.handleRequest(intentMap);
})
module.exports.handler = serverless(app);
Dialogflow Images:
I am trying to run the code on Messenger. Any help would be hugely appreciated as I am so stuck trying to get my head around this.
As it turns out, in the below image, a Custom Payload was causing the issue I was having. If you get the same error
Error: Platform can NOT be empty at new Payload.
Triple check your default responses across all the response types and remove anything that has an empty payload.
Your resolution is a little intuitive and not completely correct. It is not specifically a problem with an empty payload, the problem persists with having a payload in general.
You can try to either set the platform manually like so =>
How to set a custom platform in Dialogflow NodeJS Client
or choose one of the methods described in here =>
https://github.com/dialogflow/dialogflow-fulfillment-nodejs/issues/153
Setting the platform befor initializing the WebHookClient
if (!request.body.queryResult.fulfillmentMessages)
return;
request.body.queryResult.fulfillmentMessages = request.body.queryResult.fulfillmentMessages.map(m => {
if (!m.platform)
m.platform = 'PLATFORM_UNSPECIFIED';
return m;
});
I'm trying to run the sillyNameMaker example from actions-on-google with api.ai, on my computer.
I set up a nodejs server with express, and a ngrok tunneling. When I try to send a request with my agent on api.ai, my server receives the POST request, but the body appears to be empty. Is there anything i didn't set up properly?
Here is my index.js file:
'use strict';
var express = require('express')
var app = express()
const ApiAiAssistant = require('actions-on-google').ApiAiAssistant;
function sillyNameMaker(req, res) {
const assistant = new ApiAiAssistant({request: req, response: res});
// Create functions to handle requests here
const WELCOME_INTENT = 'input.welcome'; // the action name from the API.AI intent
const NUMBER_INTENT = 'input.number'; // the action name from the API.AI intent
const NUMBER_ARGUMENT = 'input.mynum'; // the action name from the API.AI intent
function welcomeIntent (assistant) {
assistant.ask('Welcome to action snippets! Say a number.');
}
function numberIntent (assistant) {
let number = assistant.getArgument(NUMBER_ARGUMENT);
assistant.tell('You said ' + number);
}
let actionMap = new Map();
actionMap.set(WELCOME_INTENT, welcomeIntent);
actionMap.set(NUMBER_INTENT, numberIntent);
assistant.handleRequest(actionMap);
function responseHandler (assistant) {
console.log("okok")
// intent contains the name of the intent you defined in the Actions area of API.AI
let intent = assistant.getIntent();
switch (intent) {
case WELCOME_INTENT:
assistant.ask('Welcome! Say a number.');
break;
case NUMBER_INTENT:
let number = assistant.getArgument(NUMBER_ARGUMENT);
assistant.tell('You said ' + number);
break;
}
}
// you can add the function name instead of an action map
assistant.handleRequest(responseHandler);
}
app.post('/google', function (req, res) {
console.log(req.body);
sillyNameMaker(req, res);
})
app.get('/', function (req, res) {
res.send("Server is up and running.")
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
And the error I got:
TypeError: Cannot read property 'originalRequest' of undefined
at new ApiAiAssistant (/Users/clementjoudet/Desktop/Dev/google-home/node_modules/actions-on-google/api-ai-assistant.js:67:19)
at sillyNameMaker (/Users/clementjoudet/Desktop/Dev/google-home/main.js:8:21)
I'm trying to print req.body but it is undefined... Thanks in advance for your help.
Both you and the actions-on-google package are making an assumption about how you're using Express. By default, Express does not populate the req.body attribute (see the reference for req.body). Instead it relies on additional middleware such as body-parser to do so.
You should be able to add body parser to your project with
npm install body-parser
and then use it to parse the request body into JSON (which API.AI sends and actions-on-google uses) with some additional lines right after you define app to attach it to Express:
var bodyParser = require('body-parser');
app.use(bodyParser.json());