如何在 Dialogflow 中获得用户对位置和地理坐标的确认?
How to get user confirmation for Location and the geo-coordinates in Dialogflow?
我正在使用对话流为 google 助手制作应用程序。我的应用程序使用部署在 firebase 上的 webhook 函数。主要问题是如何获取用户位置——我需要将坐标传递给 URL。我可以提示位置权限的消息,但是如何确认和获取坐标。
我需要添加任何意图来确认吗?我在默认欢迎意图中请求位置权限。
代码如下:
'use strict';
process.env.DEBUG = 'actions-on-google:*';
const App = require('actions-on-google').DialogflowApp;
const functions = require('firebase-functions');
//----global variables-----
const http = require('https');
var body = "";
var body2="";
var address="";
var latitude="";
var longitude="";
var lati="";
var long="";
//------ADDRESS_ACTION to tell the current address based on current coordinates----
const ADDRESS_ACTION = 'address_action';
//-----DIRECTION_INTENT to output the map guidence through basic card------
const DIRECTION_INTENT = 'direction_action';
//--------during welcome intent, ask the permiisions------
const DIR_PERMISS_CONF= 'intent.welcome';
exports.addressMaker = functions.https.onRequest((request, response) => {
const app = new App({request, response});
console.log('Request headers: ' + JSON.stringify(request.headers));
console.log('Request body: ' + JSON.stringify(request.body));
function welcomeIntentDirection (app)
{
app.askForPermission("Hi! Welcome to !To show address/direction",app.SupportedPermissions.DEVICE_PRECISE_LOCATION);
if (app.isPermissionGranted())
{
// app.tell('You said ' + app.getRawInput());
}
else
{
// app.tell('You said ' + app.getRawInput());
}
}
function makeDirection(app)
{
//-------here we are extraction what user said, we will extract the place name, it is for "direction to PLACE_NAME"-------
var stringRequest=JSON.stringify(request.body);
var jsonRequest=JSON.parse(stringRequest);
var jsonSpeech=jsonRequest.originalRequest.data.inputs[0].rawInputs[0].query;
var arr=jsonSpeech.split("to");
var placeName=arr[1].trim();
//------Current Location--------------
let deviceCoordinates = app.getDeviceLocation().coordinates;
latitude=deviceCoordinates.latitude;
longitude=deviceCoordinates.longitude;
//-----now send this place name to api to get coordinates-----
var req2= http.get("https://api.url-with-PARAMETER-placeName",function(res){
res.on("data", function(chunk2){ body2 += chunk2; });
res.on('end', function()
{
if (res.statusCode === 200)
{
var data2 = JSON.parse(body2);
try
{
lati=data2.geometry.lat;
long=data2.geometry.lng;
}catch(e){app.tell("Invalid or non-existent address");}
}
});
});
//-------------------Here BUILDING the CARD (Rich RESPONSE)-----------------------
app.ask(app.buildRichResponse()
// Create a basic card and add it to the rich response
.addSimpleResponse('Your directions are here:')
.addBasicCard(app.buildBasicCard()
.addButton('Start Guidence', 'https://www.google.com/maps/dir/?api=1&origin='+latitude+','+longitude+'&destination='+lati+','+long+'')
.setImage('https://png.pngtree.com/element_origin_min_pic/16/11/30/a535f05d9d512610e25a036b50da036f.jpg', 'Image alternate text')
.setImageDisplay('CROPPED')
)
);
}
function makeAddress (app)
{
let deviceCoordinates = app.getDeviceLocation().coordinates;
latitude=deviceCoordinates.latitude;
longitude=deviceCoordinates.longitude;
var req=http.get("https://api.url/reverse?coords="+latitude+","+longitude+"",
function(res)
{
res.on("data", function(chunk){ body += chunk; });
res.on('end', function()
{
if (res.statusCode === 200)
{
try
{
var data = JSON.parse(body);
address=data.words;
app.tell('Alright, your address is '+address);
} catch (e) { }
}
else
{
app.tell("Unable to contact to server +"+res.statusCode);
}
});
});
}
// d. build an action map, which maps intent names to functions
let actionMap = new Map();
actionMap.set(DIR_PERMISS_CONF,welcomeIntentDirection);
actionMap.set(ADDRESS_ACTION, makeAddress);
actionMap.set(DIRECTION_INTENT, makeDirection);
app.handleRequest(actionMap);
});
是的,您需要一个 Intent 来处理来自用户的响应。 Dialogflow 的模型是所有来自用户的响应都需要通过一个意图来处理。
在这种情况下,它将是您的 "confirmation" 意图。
您没有显示 Intent 的屏幕截图(如果您还有其他问题,请更新问题以包含它),但它需要处理事件 actions_intent_PERMISSION
。处理此事件时,您不需要任何训练短语。
我拥有的一个(具有 result.location
的 Intent 名称和 Action)如下所示:
附带说明一下,在您的处理程序中,您试图获取用户名,但除非您已请求许可,否则此名称不可用。你没有征求许可。如果你愿意,你可以在询问他们的位置的同时请求许可,例如
app.askForPermissions("To show your address", [
app.SupportedPermissions.NAME,
app.SupportedPermissions.DEVICE_PRECISE_LOCATION
]);
根据您的屏幕截图更新。
不清楚你的对话流程是什么意思,所以我做了一些假设。
但是根据您的代码,您请求的许可是出于欢迎意图。然后用户授予权限,您需要一个接受该回复的意图。
我没有看到接受该回复的意图。
为了接受回复,您需要一个处理事件的 Intent actions_intent_PERMISSION
,如我在上面的屏幕截图中所示。
您似乎也在创建上下文,尽管不清楚原因。
根据您发布的代码和您的补充评论更新。
代码中有几个问题可能会导致您遇到一些问题,而其他一些问题可能会在将来给您带来麻烦。在评论中回答您的一些问题和一些未提出的问题:
我启用了 Small Talk
这不会造成任何伤害 - 但它可能也无济于事。我建议您禁用它,直到您的对话的核心部分正常工作。
我可以得到坐标
很好 - Intent 正在运行。
但是...
我看到您将坐标存储在全局变量中。这在这种情况下有效(但不完全是,请参阅您的下一个问题),但如果不止一个人在使用该操作,则不会扩展,因为用户会话之间的值可能会混淆。
换句话说:如果您在 webhook 中使用全局变量,那么每个人都可以访问这些值。这非常糟糕。
您想要的是一种在两次调用您的 webhook 之间存储用户信息的方法。 elsewhere 涵盖了很多方法,而且可以相当复杂。但是,一般来说,您可以将值存储在上下文中或 app.data
中,它们将作为同一对话的一部分返回给您。
我第一次询问时没有得到目标地址
和
第二次询问时,有目的地址
您的函数 makeDirection()
包含一个异步操作以获取目标地址的 lat/lon,但是您正在该异步块之外发回回复。
所以发生的事情是您开始 http 调用,然后代码继续,您将回复发送给具有空目的地的用户。稍后,回调完成并设置目的地,但对于第一次调用来说为时已晚。但是当你第二次调用它时,它会经历相同的例程(开始异步调用,然后在调用完成之前立即发回一些东西),但是前一个 运行 的值被设置了,因为你已经设置了它们在全局变量中。
要解决此问题,您需要调用 app.ask()
作为异步回调的一部分。这可能看起来像这样:
http.get("https://api.url-with-PARAMETER-placeName",function(res){
res.on("data", function(chunk2){ body2 += chunk2; });
res.on('end', function(){
if (res.statusCode === 200){
var data2 = JSON.parse(body2);
try{
lati=data2.geometry.lat;
long=data2.geometry.lng;
app.ask(
app.buildRichResponse()
.addThingsToRichResponse()
);
} catch(e){
app.tell("Invalid or non-existent address");
}
}
});
});
获取用户对目的地的评价时出现问题
您尝试获取用户目的地的代码……很奇怪。获取 rawInputs
是合法的,但试图将其分解以获得特定值并不是最佳做法,尤其是因为用户可能不会准确地说出您认为他们会说的内容。例如,如果他们说 "I'm heading towards somewhere".
怎么办?
更好的方法是让您的 direction_action
Intent 采用各种不同的短语并将其中的 "important" 部分(目标)分配给一个参数。然后,在您的 webhook 中,您可以只读取参数的值并将其传递给服务以确定位置。
你也不妨看看最近发布的Place Helper,它类似于设备精确定位助手,但也可以获取用户指定的地址。
我正在使用对话流为 google 助手制作应用程序。我的应用程序使用部署在 firebase 上的 webhook 函数。主要问题是如何获取用户位置——我需要将坐标传递给 URL。我可以提示位置权限的消息,但是如何确认和获取坐标。
我需要添加任何意图来确认吗?我在默认欢迎意图中请求位置权限。
代码如下:
'use strict';
process.env.DEBUG = 'actions-on-google:*';
const App = require('actions-on-google').DialogflowApp;
const functions = require('firebase-functions');
//----global variables-----
const http = require('https');
var body = "";
var body2="";
var address="";
var latitude="";
var longitude="";
var lati="";
var long="";
//------ADDRESS_ACTION to tell the current address based on current coordinates----
const ADDRESS_ACTION = 'address_action';
//-----DIRECTION_INTENT to output the map guidence through basic card------
const DIRECTION_INTENT = 'direction_action';
//--------during welcome intent, ask the permiisions------
const DIR_PERMISS_CONF= 'intent.welcome';
exports.addressMaker = functions.https.onRequest((request, response) => {
const app = new App({request, response});
console.log('Request headers: ' + JSON.stringify(request.headers));
console.log('Request body: ' + JSON.stringify(request.body));
function welcomeIntentDirection (app)
{
app.askForPermission("Hi! Welcome to !To show address/direction",app.SupportedPermissions.DEVICE_PRECISE_LOCATION);
if (app.isPermissionGranted())
{
// app.tell('You said ' + app.getRawInput());
}
else
{
// app.tell('You said ' + app.getRawInput());
}
}
function makeDirection(app)
{
//-------here we are extraction what user said, we will extract the place name, it is for "direction to PLACE_NAME"-------
var stringRequest=JSON.stringify(request.body);
var jsonRequest=JSON.parse(stringRequest);
var jsonSpeech=jsonRequest.originalRequest.data.inputs[0].rawInputs[0].query;
var arr=jsonSpeech.split("to");
var placeName=arr[1].trim();
//------Current Location--------------
let deviceCoordinates = app.getDeviceLocation().coordinates;
latitude=deviceCoordinates.latitude;
longitude=deviceCoordinates.longitude;
//-----now send this place name to api to get coordinates-----
var req2= http.get("https://api.url-with-PARAMETER-placeName",function(res){
res.on("data", function(chunk2){ body2 += chunk2; });
res.on('end', function()
{
if (res.statusCode === 200)
{
var data2 = JSON.parse(body2);
try
{
lati=data2.geometry.lat;
long=data2.geometry.lng;
}catch(e){app.tell("Invalid or non-existent address");}
}
});
});
//-------------------Here BUILDING the CARD (Rich RESPONSE)-----------------------
app.ask(app.buildRichResponse()
// Create a basic card and add it to the rich response
.addSimpleResponse('Your directions are here:')
.addBasicCard(app.buildBasicCard()
.addButton('Start Guidence', 'https://www.google.com/maps/dir/?api=1&origin='+latitude+','+longitude+'&destination='+lati+','+long+'')
.setImage('https://png.pngtree.com/element_origin_min_pic/16/11/30/a535f05d9d512610e25a036b50da036f.jpg', 'Image alternate text')
.setImageDisplay('CROPPED')
)
);
}
function makeAddress (app)
{
let deviceCoordinates = app.getDeviceLocation().coordinates;
latitude=deviceCoordinates.latitude;
longitude=deviceCoordinates.longitude;
var req=http.get("https://api.url/reverse?coords="+latitude+","+longitude+"",
function(res)
{
res.on("data", function(chunk){ body += chunk; });
res.on('end', function()
{
if (res.statusCode === 200)
{
try
{
var data = JSON.parse(body);
address=data.words;
app.tell('Alright, your address is '+address);
} catch (e) { }
}
else
{
app.tell("Unable to contact to server +"+res.statusCode);
}
});
});
}
// d. build an action map, which maps intent names to functions
let actionMap = new Map();
actionMap.set(DIR_PERMISS_CONF,welcomeIntentDirection);
actionMap.set(ADDRESS_ACTION, makeAddress);
actionMap.set(DIRECTION_INTENT, makeDirection);
app.handleRequest(actionMap);
});
是的,您需要一个 Intent 来处理来自用户的响应。 Dialogflow 的模型是所有来自用户的响应都需要通过一个意图来处理。
在这种情况下,它将是您的 "confirmation" 意图。
您没有显示 Intent 的屏幕截图(如果您还有其他问题,请更新问题以包含它),但它需要处理事件 actions_intent_PERMISSION
。处理此事件时,您不需要任何训练短语。
我拥有的一个(具有 result.location
的 Intent 名称和 Action)如下所示:
附带说明一下,在您的处理程序中,您试图获取用户名,但除非您已请求许可,否则此名称不可用。你没有征求许可。如果你愿意,你可以在询问他们的位置的同时请求许可,例如
app.askForPermissions("To show your address", [
app.SupportedPermissions.NAME,
app.SupportedPermissions.DEVICE_PRECISE_LOCATION
]);
根据您的屏幕截图更新。
不清楚你的对话流程是什么意思,所以我做了一些假设。
但是根据您的代码,您请求的许可是出于欢迎意图。然后用户授予权限,您需要一个接受该回复的意图。
我没有看到接受该回复的意图。
为了接受回复,您需要一个处理事件的 Intent actions_intent_PERMISSION
,如我在上面的屏幕截图中所示。
您似乎也在创建上下文,尽管不清楚原因。
根据您发布的代码和您的补充评论更新。
代码中有几个问题可能会导致您遇到一些问题,而其他一些问题可能会在将来给您带来麻烦。在评论中回答您的一些问题和一些未提出的问题:
我启用了 Small Talk
这不会造成任何伤害 - 但它可能也无济于事。我建议您禁用它,直到您的对话的核心部分正常工作。
我可以得到坐标
很好 - Intent 正在运行。
但是...
我看到您将坐标存储在全局变量中。这在这种情况下有效(但不完全是,请参阅您的下一个问题),但如果不止一个人在使用该操作,则不会扩展,因为用户会话之间的值可能会混淆。
换句话说:如果您在 webhook 中使用全局变量,那么每个人都可以访问这些值。这非常糟糕。
您想要的是一种在两次调用您的 webhook 之间存储用户信息的方法。 elsewhere 涵盖了很多方法,而且可以相当复杂。但是,一般来说,您可以将值存储在上下文中或 app.data
中,它们将作为同一对话的一部分返回给您。
我第一次询问时没有得到目标地址 和 第二次询问时,有目的地址
您的函数 makeDirection()
包含一个异步操作以获取目标地址的 lat/lon,但是您正在该异步块之外发回回复。
所以发生的事情是您开始 http 调用,然后代码继续,您将回复发送给具有空目的地的用户。稍后,回调完成并设置目的地,但对于第一次调用来说为时已晚。但是当你第二次调用它时,它会经历相同的例程(开始异步调用,然后在调用完成之前立即发回一些东西),但是前一个 运行 的值被设置了,因为你已经设置了它们在全局变量中。
要解决此问题,您需要调用 app.ask()
作为异步回调的一部分。这可能看起来像这样:
http.get("https://api.url-with-PARAMETER-placeName",function(res){
res.on("data", function(chunk2){ body2 += chunk2; });
res.on('end', function(){
if (res.statusCode === 200){
var data2 = JSON.parse(body2);
try{
lati=data2.geometry.lat;
long=data2.geometry.lng;
app.ask(
app.buildRichResponse()
.addThingsToRichResponse()
);
} catch(e){
app.tell("Invalid or non-existent address");
}
}
});
});
获取用户对目的地的评价时出现问题
您尝试获取用户目的地的代码……很奇怪。获取 rawInputs
是合法的,但试图将其分解以获得特定值并不是最佳做法,尤其是因为用户可能不会准确地说出您认为他们会说的内容。例如,如果他们说 "I'm heading towards somewhere".
更好的方法是让您的 direction_action
Intent 采用各种不同的短语并将其中的 "important" 部分(目标)分配给一个参数。然后,在您的 webhook 中,您可以只读取参数的值并将其传递给服务以确定位置。
你也不妨看看最近发布的Place Helper,它类似于设备精确定位助手,但也可以获取用户指定的地址。