首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >DialogFlow修改的'Bike Shop‘示例不起作用

DialogFlow修改的'Bike Shop‘示例不起作用
EN

Stack Overflow用户
提问于 2019-03-15 05:19:54
回答 1查看 597关注 0票数 2

我在使用这个Bike Shop示例的修改版本(或原始版本)时遇到了一些严重的困难。

我试图复制基本功能,但添加姓名,电话号码,电子邮件等字段到日历事件。

也许这是因为这是我第一次参加Node.js的聚会,但事实证明,这比泡在热水泥里更不愉快。

我将快速总结一下我遇到的主要问题:

获取要在日历中填充的事件

所以我已经把所有事情都理顺了,多亏了@Prisoner。我的主要问题是我的上下文是大写的,因此无法识别。将大多数意图转换为顶级意图也很有帮助。现在,我可以始终让实现响应填写并给出我的第一条确认消息,但我总是得到第二个“错误”响应,并且日历中没有任何事件。

日历上没有显示如何更新googleapis库?只需在package.json中将^27更改为^30?

Context不是.getContext()弃用的吗?这就是它在这里显示的(https://dialogflow.com/docs/contexts/contexts-fulfillment),这也是我昨天尝试它时错误显示的内容。

使用连字符的实体我更改了名称(我不知道名称不仅仅是一个标签),但为了将来的参考和清晰起见,它是.parameters.properties还是.params

仅供参考,这是我的意图流程:>

FirstLast (获取名字和姓氏,分配给系统实体)>

ServiceNeeded (获取所需服务,分配给开发者实体)>

date time MeetingPlace Email PhoneN (获取日期、时间、电话号码和电子邮件:分配给系统实体;获取位置:分配给开发人员实体)

我的index.js:

代码语言:javascript
复制
/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * 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
 *
 *      http://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.
 */

'use strict';

const functions = require('firebase-functions');
const {google} = require('googleapis');
const {WebhookClient} = require('dialogflow-fulfillment');

// Enter your calendar ID and service account JSON below.
// See https://github.com/dialogflow/bike-shop/blob/master/README.md#calendar-setup
const calendarId = 'm6t2gekgnv2qro9ln8genh10o8@group.calendar.google.com'; // Example: 6ujc6j6rgfk02cp02vg6h38cs0@group.calendar.google.com
const serviceAccount = {
  "type": "service_account",
  "project_id": "appsoft-lead-net",
  "private_key_id": "863c577bd9249f09cbce540cf082fb7c1f7349ac",
  "private_key": "-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----\n",
  "client_email": "id-appsoft-chatbot-v1-calendar@appsoft-lead-net.iam.gserviceaccount.com",
  "client_id": "113420745582984409565",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/id-appsoft-chatbot-v1-calendar%40appsoft-lead-net.iam.gserviceaccount.com"
}; // The JSON object looks like: { "type": "service_account", ... }

// Set up Google Calendar service account credentials
const serviceAccountAuth = new google.auth.JWT({
  email: serviceAccount.client_email,
  key: serviceAccount.private_key,
  scopes: 'https://www.googleapis.com/auth/calendar'
});

const calendar = google.calendar('v3');
process.env.DEBUG = 'dialogflow:*'; // It enables lib debugging statements

const timeZone = 'America/New_York';  // Change it to your time zone

exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
  const agent = new WebhookClient({ request, response });

  // This function receives the date and time values from the context 'MakeAppointment-followup'
  // and calls the createCalendarEvent() function to mark the specified time slot on Google Calendar.
  function makeAppointment (agent) {
    // Get the contexts
    const contextF = agent.context.get('firstlast');
    const contextS = agent.context.get('serviceneeded');
    const contextD = agent.context.get('datetimemeetingplaceemailphonen-followup');
    // This variable needs to hold an instance of Date object that specifies the start time of the appointment.
    const dateTimeStart = convertTimestampToDate(contextD.parameters.date, contextD.parameters.time);
    // This variable holds the end time of the appointment, which is calculated by adding an hour to the start time.
    const dateTimeEnd = addHours(dateTimeStart, 1);
    // Convert the Date object into human-readable strings.
    const appointmentTimeString = getLocaleTimeString(dateTimeStart);
    const appointmentDateString = getLocaleDateString(dateTimeStart);
// set properties to variables
const appointmentLocationString = contextD.parameters.meetingPlace;
const appointmentEmail = contextD.parameters.email;
const appointmentService = contextS.parameters.ServiceNeeded;
const appointmentFullName = contextF.parameters.givenName + " " + contextF.parameters.lastName;
const appointmentFirstName = contextF.parameters.givenName;
const appointmentPhoneString = contextD.parameters.phoneNumber;
    // Delete the context 'MakeAppointment-followup'; this is the final step of the path.
    agent.context.delete('datetimemeetingplaceemailphonen-followup');
    // The createCalendarEvent() function checks the availability of the time slot and marks the time slot on Google Calendar if the slot is available.
    return createCalendarEvent(agent, dateTimeStart, dateTimeEnd, appointmentFullName, appointmentPhoneString, appointmentLocationString, appointmentEmail, appointmentService).then(() => {
        agent.context.delete('serviceneeded');
        agent.context.delete('firstlast');
        agent.context.delete('schedule');
      agent.add(`Got it! I have your appointment scheduled on ${appointmentDateString} at ${appointmentTimeString}—we'll contact you shortly to confirm the deets! See you soon, ${appointmentFirstName}. Good-bye!`);
    }).catch(() => {
      agent.add(`Sorry, ${appointmentFirstName}, something went wrong—I couldn't book ${appointmentDateString} at ${appointmentTimeString}. Try trying again! If that doesn't work, let us know—Mitch probably just messed up something...`);
    });
  }

  // This function receives the date and time values from the context 'MakeAppointment-followup'
  // and calls the checkCalendarAvailablity() function to check the availability of the time slot on Google Calendar.
  function checkAppointment (agent) {
      // Get the contexts
      const contextF = agent.context.get('firstlast');
      const contextS = agent.context.get('serviceneeded');
    // This variable needs to hold an instance of Date object that specifies the start time of the appointment.
    const dateTimeStart = convertTimestampToDate(agent.parameters.date, agent.parameters.time);
    // This variable holds the end time of the appointment, which is calculated by adding an hour to the start time.
    const dateTimeEnd = addHours(dateTimeStart, 1);
    // Convert the Date object into human-readable strings.
    const appointmentTimeString = getLocaleTimeString(dateTimeStart);
    const appointmentDateString = getLocaleDateString(dateTimeStart);
    // set properties into variables
    const appointmentLocationString = agent.parameters.meetingPlace;
    const appointmentEmail = agent.parameters.email;
    const appointmentService = contextS.parameters.ServiceNeeded;
    const appointmentFullName = contextF.parameters.givenName + " " + contextF.parameters.lastName;
    const appointmentFirstName = contextF.parameters.givenName;
    const appointmentPhoneString = agent.parameters.phoneNumber;
    // The checkCalendarAvailablity() function checks the availability of the time slot.
    return checkCalendarAvailablity(dateTimeStart, dateTimeEnd).then(() => {
        // The time slot is available.
       // The function returns a response that asks for the confirmation of the date and time.
       agent.add(`Okay, ${appointmentFullName}, so you've said that you'd like your appointment on ${appointmentDateString} at ${appointmentTimeString}. We'll call ${appointmentPhoneString} and/or email ${appointmentEmail} to confirm this appointment ${appointmentLocationString} about ${appointmentService}. Did I get that right?`);
     }).catch(() => {
       // The time slot is not available.
       agent.add(`Sorry, ${appointmentFirstName}, we're booked up on ${appointmentDateString} at ${appointmentTimeString}. Huge bummer, I know =/ But is there another time you'd like to schedule your appointment?`);
       // Delete the context 'MakeAppointment-followup' to return the flow of conversation to the beginning.
       agent.context.delete('datetimemeetingplaceemailphonen-followup');
   });
  }
  // Mapping of the functions to the agent's intents.
  let intentMap = new Map();
  intentMap.set('Date Time MeetingPlace Email PhoneN', checkAppointment);
  intentMap.set('Date Time MeetingPlace Email PhoneN - yes', makeAppointment);
  agent.handleRequest(intentMap);
});

// This function checks for the availability of the time slot, which starts at 'dateTimeStart' and ends at 'dateTimeEnd'.
// 'dateTimeStart' and 'dateTimeEnd' are instances of a Date object.
function checkCalendarAvailablity (dateTimeStart, dateTimeEnd) {
  return new Promise((resolve, reject) => {
    calendar.events.list({
      auth: serviceAccountAuth, // List events for time period
      calendarId: calendarId,
      timeMin: dateTimeStart.toISOString(),
      timeMax: dateTimeEnd.toISOString()
    }, (err, calendarResponse) => {
      // Check if there is an event already on the Calendar
      if (err || calendarResponse.data.items.length > 0) {
        reject(err || new Error('Requested time conflicts with another appointment'));
      }else {
        resolve(calendarResponse);
      }
    });
  });
}

// This function marks the time slot on Google Calendar. The time slot on the calendar starts at 'dateTimeStart' and ends at 'dateTimeEnd'.
// 'dateTimeStart' and 'dateTimeEnd' are instances of a Date object.
function createCalendarEvent (agent, dateTimeStart, dateTimeEnd, appointmentFullName, appointmentPhoneString, appointmentLocationString, appointmentEmail, appointmentService) {

// assign values to variables
    appointmentPhoneString = agent.parameters.phoneNumber;
    appointmentLocationString = agent.parameters.meetingPlace;
    appointmentEmail = agent.parameters.email;
    appointmentService = agent.parameters.ServiceNeeded;
    appointmentFullName = agent.parameters.givenName + " " + agent.parameters.lastName;

  return new Promise((resolve, reject) => {
    calendar.events.list({
      auth: serviceAccountAuth, // List events for time period
      calendarId: calendarId,
      timeMin: dateTimeStart.toISOString(),
      timeMax: dateTimeEnd.toISOString()
    }, (err, calendarResponse) => {
      // Check if there is an event already on the Calendar
      if (err || calendarResponse.data.items.length > 0) {
        reject(err || new Error('Requested time conflicts with another appointment'));
      } else {
        // Create event for the requested time period
        calendar.events.insert({ auth: serviceAccountAuth,
          calendarId: calendarId,
          resource: {
           summary: 'Appsoft Appointment',
           start: {
             dateTime: dateTimeStart
           },
           end: {
             dateTime: dateTimeEnd
           },
           attendees:[ {
             displayName: appointmentFullName,
             email: appointmentEmail,
           }],
           location: appointmentLocationString,
           description: 'Phone Number: ' + appointmentPhoneString + '; Service Needed: ' + appointmentService}
        }, (err, event) => {
          err ? reject(err) : resolve(event);
        }
        );
      }
    });
  });
}

// A helper function that receives Dialogflow's 'date' and 'time' parameters and creates a Date instance.
function convertTimestampToDate(date, time){
  // Parse the date, time, and time zone offset values from the input parameters and create a new Date object
  return new Date(Date.parse(date.split('T')[0] + 'T' + time.split('T')[1].split('-')[0] + '-' + time.split('T')[1].split('-')[1]));
}

// A helper function that adds the integer value of 'hoursToAdd' to the Date instance 'dateObj' and returns a new Data instance.
function addHours(dateObj, hoursToAdd){
  return new Date(new Date(dateObj).setHours(dateObj.getHours() + hoursToAdd));
}

// A helper function that converts the Date instance 'dateObj' into a string that represents this time in English.
function getLocaleTimeString(dateObj){
  return dateObj.toLocaleTimeString('en-US', { hour: 'numeric', hour12: true, timeZone: timeZone });
}

// A helper function that converts the Date instance 'dateObj' into a string that represents this date in English.
function getLocaleDateString(dateObj){
  return dateObj.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', timeZone: timeZone });
}

我的package.json:

代码语言:javascript
复制
{
  "name": "DialogflowFirebaseWebhook",
  "description": "Firebase Webhook dependencies for a Dialogflow agent.",
  "version": "0.0.1",
  "private": true,
  "license": "Apache Version 2.0",
  "author": "Google Inc.",
  "engines": {
    "node": "6"
  },
  "scripts": {
    "lint": "semistandard --fix \"**/*.js\"",
    "start": "firebase deploy --only functions",
    "deploy": "firebase deploy --only functions"
  },
  "dependencies": {
    "firebase-functions": "^2.0.2",
    "firebase-admin": "^5.13.1",
    "googleapis": "^27.0.0",
    "actions-on-google": "2.2.0",
    "dialogflow-fulfillment": "0.6.1"
  }
}

提前感谢您的帮助!

{修复编辑:终于让它工作了,原来上下文名称需要完全小写才能识别。另外,我在createCalendarEvent中无缘无故地错误地声明了变量。}

EN

回答 1

Stack Overflow用户

发布于 2019-03-15 09:26:44

有很多问题,所以让我们看看我们可以解决哪些问题。

拆分无法读取属性“”“”

第192行是

代码语言:javascript
复制
return new Date(Date.parse(date.split('T')[0] + 'T' + time.split('T')[1].split('-')[0] + '-' + time.split('T')[1].split('-')[1]));

它有很多对split()的调用,所以不清楚到底是哪一个导致了问题,但这是因为datetime (或两者)都没有定义。

包含此行的convertTimestampToDate()函数是从两个不同的位置调用的。您的makeAppointment()checkAppointment()函数,这两个函数似乎具有相同的行

代码语言:javascript
复制
    const dateTimeStart = convertTimestampToDate(agent.parameters.date, agent.parameters.time);

您没有显示Schedule appointment - Date Time meetingPlaceSchedule appointment - Date Time meetingPlace - yes意图配置本身,但听起来它们中的一个实际上并没有datetime参数。

来自fulfillment的响应

您对哪些响应没有生成有点模糊,但对于具有处理程序的两个意图,响应似乎设置正确。

在以下任何情况下都会使用UI中的参数:

未为该Intent.

  • Fulfillment显式添加回复的
  • Fulfillment启用。

每条消息有640个字符的限制,但我不认为你的回复达到了这个限制,尽管我认为在某些情况下这可能是可能的。

动态参数是未定义的

我不确定你所说的“动态”是什么意思,但这听起来像是datetime没有定义一样的问题。同样,我会检查以确保这些参数是按照您认为的意图发送的。

从Intent "flow“(我将在下面讨论)中,听起来您希望参数不断地从Intent填充到Intent。通常,参数是从当前意图中提供的参数。

如果您设置了输出上下文,则还可以在上下文中设置参数,这将延续到将来的意图。因此,您可能希望从上下文获取它们,而不是从参数获取。

根据下面sid的评论进行更新:如果你想在当前的Intent参数中使用过去的参数,你需要在Intent参数部分使用类似#output-context.param的东西从上下文中显式地设置你的Intent中的值。

日历上未显示

您使用的是相当旧的googleapis库版本,这可能是问题所在,也可能不是。然而,较新的应用程序本身就支持Promises,这肯定会使您的代码更容易使用。

我不确定这就是它,但是您指定了一个名为resource的属性,该属性包含events资源(根据specification),并且应该在请求的主体中。我不知道这是否与您正在使用的库有所不同,但在current library中,此字段应称为requestBody

Context

处理上下文的最佳方法是使用直接访问器方法,如agent.getContext()agent.setContext()agent.clearContext()

你可能已经看过一些关于谷歌上的操作的旧文档,其中谈到了conv.contexts,但在这种情况下不适用。

带有连字符实体

最简单的方法是将Intent UI中的参数名称更改为不带连字符的名称。你可以随心所欲地给它们命名--你不必根据实体类型来命名它们。

如果您确实希望保留连字符,那么可以根据agent.properties对象对它们进行索引(看起来您就是这样做的)。所以

代码语言:javascript
复制
agent.properties['what-ever']

意图流程

您没有显示意图配置,但听起来您有这些问题作为一系列后续意图。

这..。并不总是一个好主意。对话并不总是线性的,用户可能会尝试在对话中后退(或前进),这是后续意图不能很好处理的。

更好的解决方案是将这些意图中的大多数作为顶层,所有这些意图都使用实现。意图负责将参数传递给fulfillment,fulfillment存储这些参数,然后确定哪些值仍然缺失并请求一个。如果你觉得你需要缩小你对用户的期望,你可以考虑使用上下文,但这并不像你想象的那么有必要。(有关这方面的讨论,请参阅Thinking for Voice: Design Conversations not Logic。)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55172189

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档