실습: Dialogflow 및 Actions on Google을 사용하여 Google 어시스턴트용 TV 가이드 작업 만들기

1. 소개

TV를 시청 중인데 리모컨을 찾을 수 없거나 TV에 괜찮은 콘텐츠가 있는지 알아보기 위해 각 TV 채널을 방문하고 싶지 않으신가요? Google 어시스턴트에게 TV에 무엇이 있는지 물어보세요. 이 실습에서는 Dialogflow를 사용하여 간단한 작업을 빌드하고 Google 어시스턴트와 이를 통합하는 방법을 알아봅니다.

이 실습은 일반적인 클라우드 개발자 경험을 반영하여 순서대로 진행됩니다.

  1. Dialogflow v2 에이전트 만들기
  2. 커스텀 항목 만들기
  3. 인텐트 만들기
  4. Firebase Functions로 웹훅 설정
  5. 챗봇 테스트
  6. Google 어시스턴트 통합 사용 설정

빌드할 항목

Google 어시스턴트용 양방향 TV 가이드 챗봇 에이전트를 빌드할 예정입니다. TV 가이드에게 현재 특정 채널에서 방송 중인 콘텐츠를 물어볼 수 있습니다. 예: 'MTV에서 방영 중인 프로그램' TV 가이드 작업을 통해 현재 재생 중인 콘텐츠와 다음에 방영되는 콘텐츠를 확인할 수 있습니다.

학습할 내용

  • Dialogflow v2로 챗봇을 만드는 방법
  • Dialogflow로 커스텀 항목을 만드는 방법
  • Dialogflow로 선형 대화를 만드는 방법
  • Dialogflow 및 Firebase Functions로 웹훅 처리를 설정하는 방법
  • Actions on Google을 통해 애플리케이션을 Google 어시스턴트로 가져오는 방법

기본 요건

  • Dialogflow 에이전트를 만들려면 Google ID / Gmail 주소가 필요합니다.
  • 자바스크립트에 관한 기본 지식은 필요하지 않지만 웹훅 처리 코드를 변경하려는 경우 유용할 수 있습니다.

2. 설정

브라우저에서 웹 활동 사용 설정

  1. http://myaccount.google.com/activitycontrols를 클릭합니다.

  1. 웹 및 앱 활동이 사용 설정된 경우:

bf8d16b828d6f79a.png

Dialogflow 에이전트 만들기

  1. https://console.dialogflow.com을 엽니다.

  1. 왼쪽 표시줄의 로고 바로 아래에서 'Create New Agent'를 선택합니다. 기존 에이전트가 있는 경우 먼저 드롭다운을 클릭합니다.

1d7c2b56a1ab95b8.png

  1. 에이전트 이름 지정: your-name-tvguide (사용자 이름 사용)

35237b5c5c539ecc.png

  1. 기본 언어로 English - en을 선택합니다.
  2. 기본 시간대로 가장 가까운 시간대를 선택합니다.
  3. 만들기를 클릭합니다.

Dialogflow 구성

  1. 왼쪽 메뉴에서 프로젝트 이름 옆에 있는 톱니바퀴 아이콘을 클릭합니다.

1d7c2b56a1ab95b8.png

  1. 상담사 설명을 My TV Guide로 입력합니다.

26f262d359c49075.png

  1. 로그 설정까지 아래로 스크롤하고 두 스위치를 모두 전환하여 Dialogflow의 상호작용을 로깅하고 모든 상호작용을 Google Cloud Stackdriver에 로깅합니다. 나중에 작업을 디버그할 때 필요합니다.

e80c17acc3cce993.png

  1. 저장을 클릭합니다.

Actions on Google 구성

  1. 오른쪽 패널의 Google 어시스턴트에서 어떻게 작동하는지 알아보기에서 Google 어시스턴트 링크를 클릭합니다.

5a4940338fc351e3.png

그러면 http://console.actions.google.com이 열립니다.

Actions on Google을 처음 사용하는 경우 먼저 다음 양식을 작성해야 합니다.

3fd4e594fa169072.png

  1. 프로젝트 이름을 클릭하여 시뮬레이터**에서 작업을 열어봅니다.**
  2. 메뉴 바에서 테스트를 선택합니다.

6adb83ffb7adeb78.png

  1. 시뮬레이터가 English로 설정되어 있는지 확인하고 Talk to my test-app을 클릭합니다.

기본적인 Dialogflow 기본 인텐트와 함께 작업이 시작됩니다. 이는 Action on Google과의 통합 설정이 완료되었다는 의미입니다.

3. 커스텀 개체

항목이란 앱 또는 기기에서 작업을 수행하는 객체입니다. 이를 매개변수 / 변수로 생각하세요. TV 가이드에서 'MTV에서 방영 중인 콘텐츠'라고 묻습니다. MTV는 항목이자 변수입니다. '내셔널 지오그래픽'과 같은 다른 채널도 요청할 수 있습니다. 또는 '코미디 센트럴'입니다. 수집된 개체는 TV Guide API 웹 서비스에 대한 요청에서 매개변수로 사용됩니다.

여기에서 Dialogflow 항목에 대해 자세히 알아보세요.

채널 항목 만들기

  1. Dialogflow 콘솔의 메뉴 항목에서 Entities(항목)를 클릭합니다.
  2. 항목 만들기를 클릭합니다.
  3. 항목 이름: channel (모두 소문자여야 함)
  4. 채널 이름을 전달합니다. (Google 어시스턴트가 다른 내용을 이해할 수 있도록 동의어가 필요한 채널도 있습니다.) 입력하는 동안 Tab과 Enter 키를 사용할 수 있습니다. 채널 번호를 참조 값으로 입력합니다. 채널 이름은 다음과 같은 동의어로 지정됩니다.
  • 1 - 1, Net 1, Net Station 1

ee4e4955aa77232d.png

5**.** 파란색 저장 버튼 옆에 있는 메뉴 버튼을 클릭하여 **Raw Edit** 모드로 전환합니다.

e294b49b123e034f.png

  1. 복사 및 다른 항목을 CSV 형식으로 붙여넣습니다.
"2","2","Net 2, Net Station 2"
"3","3","Net 3, Net Station 3"
"4","4","RTL 4"
"5","5","Movie Channel"
"6","6","Sports Channel"
"7","7","Comedy Central"
"8","8","Cartoon Network"
"9","9","National Geographic"
"10","10","MTV"

ed78514afd5badef.png

  1. 저장을 클릭합니다.

4. 인텐트

Dialogflow인텐트를 사용하여 사용자의 의도를 분류합니다. 인텐트에는 사용자가 에이전트에게 할 수 있는 말의 예시인 학습 문구가 있습니다. 예를 들어 TV에 무엇이 나오는지 알고 싶은 사용자는 "오늘 TV에서 뭐 해?"라고 물어볼 수 있습니다. "지금 재생 중인 거 뭐야?"라고 말하거나 "tvguide"라고 말하세요.

사용자가 무언가를 쓰거나 말하면(사용자 표현이라고 함) Dialogflow는 사용자 표현을 에이전트에서 가장 유사한 인텐트와 일치시킵니다. 인텐트 일치를 인텐트 분류라고도 합니다.

Dialogflow 인텐트에 대해 자세히 알아보세요.

기본 시작 인텐트 수정

새 Dialogflow 에이전트를 만들면 기본 인텐트 두 개가 자동으로 생성됩니다. 기본 시작 인텐트는 에이전트와 대화를 시작할 때 가장 먼저 접하게 되는 흐름입니다. 기본 대체 인텐트는 에이전트가 사용자를 이해할 수 없거나 인텐트를 방금 말한 내용과 일치시킬 수 없을 때 받게 되는 흐름입니다.

  1. 기본 시작 인텐트를 클릭합니다.

Google 어시스턴트의 경우 기본 시작 인텐트로 자동 시작됩니다. 이는 Dialogflow가 환영 이벤트를 수신 대기하기 때문입니다. 그러나 입력된 학습 문구 중 하나를 말하여 인텐트를 호출할 수도 있습니다.

6beee64e8910b85d.png

다음은 기본 시작 인텐트의 환영 메시지입니다.

사용자

에이전트

"Ok Google, your-name-tvguide와 대화해 줘."

"환영합니다. 저는 TV 가이드 상담사입니다. 현재 TV 채널에서 재생 중인 콘텐츠를 알려드릴 수 있습니다. 예를 들어, MTV에서 뭐 해 봐."

  1. 응답까지 아래로 스크롤합니다.
  2. 모든 텍스트 응답을 삭제합니다.
  3. 다음 인사말을 포함하는 새로운 텍스트 응답을 하나 만듭니다.

Welcome, I am the TV Guide agent. I can tell you what's currently playing on a TV channel. For example, you can ask me: What's on MTV?

84a1110a7f7edba2.png

  1. 저장을 클릭합니다.

임시 테스트 인텐트 만들기

나중에 웹훅을 테스트할 수 있도록 테스트 목적으로 임시 테스트 인텐트를 만듭니다.

  1. 인텐트 메뉴 항목을 다시 클릭합니다.
  2. 인텐트 만들기를 클릭합니다.
  3. 인텐트 이름 Test Intent을 입력합니다. (대문자 T와 대문자 I를 사용해야 합니다. - 인텐트의 철자를 다르게 입력하면 백엔드 서비스가 작동하지 않습니다.)

925e02caa4de6b99.png

  1. 학습 문구 추가를 클릭합니다.
  • Test my agent
  • Test intent

2e44ddb2fae3c841.png

  1. 처리 > Fulfillment 사용 설정

7eb73ba04d76140e.png

이번에는 응답을 하드코딩하지 않습니다. Cloud 함수에서 응답을 제공합니다.

  1. 이 인텐트에 웹훅 호출 사용 설정 스위치를 전환합니다.

748a82d9b4d7d253.png

  1. 저장을 클릭합니다.

채널 인텐트 만들기

채널 인텐트에는 대화의 다음 부분이 포함됩니다.

사용자

에이전트

" Comedy Central에서 뭐 해?"

""현재 오후 6시에 코미디 센트럴에서 심슨가족을 상영합니다. 그 후 오후 7시에 Family Guy가 시작됩니다."

  1. 인텐트 메뉴 항목을 다시 클릭합니다.
  2. 인텐트 만들기를 클릭합니다.
  3. 인텐트 이름 Channel Intent을 입력합니다. (대문자 T와 대문자 I를 사용해야 합니다. - 인텐트의 철자를 다르게 입력하면 백엔드 서비스가 작동하지 않습니다.)
  4. 학습 문구 추가를 클릭하고 다음을 추가합니다.
  • What's on MTV?
  • What's playing on Comedy Central?
  • What show will start at 8 PM on National Geographic?
  • What is currently on TV?
  • What is airing now.
  • Anything airing on Net Station 1 right now?
  • What can I watch at 7 PM?
  • What's on channel MTV?
  • What's on TV?
  • Please give me the tv guide.
  • Tell me what is on television.
  • What's on Comedy Central from 10 AM?
  • What will be on tv at noon?
  • Anything on National Geographic?
  • TV Guide

6eee02db02831397.png

  1. 작업 및 매개변수까지 아래로 스크롤합니다.

b7e917026760218a.png

@channel 및 Dialogflow에 알려진 @sys.time 항목. 나중에 웹훅에서 매개변수 이름과 매개변수 값이 웹 서비스로 전송됩니다. 예를 들면 다음과 같습니다.

channel=8

time=2020-01-29T19:00:00+01:00

  1. 채널필요함으로 표시

TV 가이드 에이전트와 대화할 때는 항상 슬롯 매개변수 이름 channel을 입력해야 합니다. 대화를 시작할 때 채널 이름이 언급되지 않은 경우 Dialogflow는 모든 매개변수 슬롯을 채울 때까지 추가 질문을 합니다. 6f36973fd789c182.png

프롬프트로 다음을 입력합니다.

  • For which TV channel do you want to hear the tv guide information?
  • In which TV channel are you interested?

cdb5601ead9423f8.png

  1. 시간 매개변수를 필요에 따라 설정하지 마세요.

시간은 선택사항입니다. 시간을 지정하지 않으면 웹 서비스에서 현재 시간을 반환합니다.

  1. Fulfillment를 클릭합니다.

이번에는 응답을 하드코딩하지 않습니다. Cloud 함수에서 응답을 제공합니다. 따라서 이 인텐트의 웹훅 호출 사용 설정 스위치를 전환합니다.

  1. 저장을 클릭합니다.

5. 웹훅 fulfillment

에이전트에 정적 인텐트 응답 이상이 필요한 경우 fulfillment를 사용하여 웹 서비스를 에이전트에 연결해야 합니다. 웹 서비스를 연결하면 사용자 표현에 따라 조치를 취하고 사용자에게 동적 응답을 보낼 수 있습니다. 예를 들어 사용자가 MTV의 TV 일정을 수신하려는 경우 웹 서비스에서 데이터베이스를 확인하여 사용자에게 MTV 일정을 응답할 수 있습니다.

  1. 기본 메뉴에서 Fulfillment를 클릭합니다.
  2. 인라인 편집기 스위치 사용 설정

cc84351f0d03ab6f.png

간단한 웹훅 테스트 및 구현의 경우 인라인 편집기를 사용할 수 있습니다. 이는 서버리스 Firebase용 Cloud Functions를 사용합니다.

  1. 편집기에서 index.js 탭을 클릭하고 Node.js 코드용 JavaScript를 복사하여 붙여넣습니다.
'use strict';

process.env.DEBUG = 'dialogflow:debug';

const {
  dialogflow,
  BasicCard,
  Button,
  Image,
  List
 } = require('actions-on-google');

const functions = require('firebase-functions');
const moment = require('moment');
const TVGUIDE_WEBSERVICE = 'https://tvguide-e4s5ds5dsa-ew.a.run.app/channel';
const { WebhookClient } = require('dialogflow-fulfillment');
var spokenText = '';
var results = null;


/* When the Test Intent gets invoked. */
function testHandler(agent) {
    let spokenText = 'This is a test message, when you see this, it means your webhook fulfillment worked!';

    if (agent.requestSource === agent.ACTIONS_ON_GOOGLE) {
        let conv = agent.conv();
        conv.ask(spokenText);
        conv.ask(new BasicCard({
            title: `Test Message`,
            subTitle: `Dialogflow Test`,
            image: new Image({
                url: 'https://dummyimage.com/600x400/000/fff',
                alt: 'Image alternate text',
            }),
            text: spokenText,
            buttons: new Button({
                title: 'This is a button',
                url: 'https://assistant.google.com/',
            }),
        }));
        // Add Actions on Google library responses to your agent's response
        agent.add(conv);
    } else {
        agent.add(spokenText);
    }
}

/* When the Channel Intent gets invoked. */
function channelHandler(agent) {
    var jsonResponse = `{"ID":10,"Listings":[{"Title":"Catfish Marathon","Date":"2018-07-13","Time":"11:00:00"},{"Title":"Videoclips","Date":"2018-07-13","Time":"12:00:00"},{"Title":"Pimp my ride","Date":"2018-07-13","Time":"12:30:00"},{"Title":"Jersey Shore","Date":"2018-07-13","Time":"13:00:00"},{"Title":"Jersey Shore","Date":"2018-07-13","Time":"13:30:00"},{"Title":"Daria","Date":"2018-07-13","Time":"13:45:00"},{"Title":"The Real World","Date":"2018-07-13","Time":"14:00:00"},{"Title":"The Osbournes","Date":"2018-07-13","Time":"15:00:00"},{"Title":"Teenwolf","Date":"2018-07-13","Time":"16:00:00"},{"Title":"MTV Unplugged","Date":"2018-07-13","Time":"16:30:00"},{"Title":"Rupauls Drag Race","Date":"2018-07-13","Time":"17:30:00"},{"Title":"Ridiculousness","Date":"2018-07-13","Time":"18:00:00"},{"Title":"Punk'd","Date":"2018-07-13","Time":"19:00:00"},{"Title":"Jersey Shore","Date":"2018-07-13","Time":"20:00:00"},{"Title":"MTV Awards","Date":"2018-07-13","Time":"20:30:00"},{"Title":"Beavis & Butthead","Date":"2018-07-13","Time":"22:00:00"}],"Name":"MTV"}`;
    var results = JSON.parse(jsonResponse);
    var listItems = {};
    spokenText = getSpeech(results);

    for (var i = 0; i < results['Listings'].length; i++) {
        listItems[`SELECT_${i}`] = {
            title: `${getSpokenTime(results['Listings'][i]['Time'])} - ${results['Listings'][i]['Title']}`,
            description: `Channel: ${results['Name']}`
        }
    }
    if (agent.requestSource === agent.ACTIONS_ON_GOOGLE) {
        let conv = agent.conv();
        conv.ask(spokenText);
        conv.ask(new List({
            title: 'TV Guide',
            items: listItems
        }));
        // Add Actions on Google library responses to your agent's response
        agent.add(conv);
    } else {
        agent.add(spokenText);
    }
}

/**
 * Return a text string to be spoken out by the Google Assistant
 * @param {object} JSON tv results
 */
var getSpeech = function(tvresults) {
    let s = "";
    if(tvresults['Listings'][0]) {
        let channelName = tvresults['Name'];
        let currentlyPlayingTime = getSpokenTime(tvresults['Listings'][0]['Time']);
        let laterPlayingTime = getSpokenTime(tvresults['Listings'][1]['Time']);
        s = `On ${channelName} from ${currentlyPlayingTime}, ${tvresults['Listings'][0]['Title']} is playing.
        Afterwards at ${laterPlayingTime}, ${tvresults['Listings'][1]['Title']} will start.`
    }

    return s;
}

/**
 * Return a natural spoken time
 * @param {string} time in 'HH:mm:ss' format
 * @returns {string} spoken time (like 8 30 pm i.s.o. 20:00:00)
 */
var getSpokenTime = function(time){
    let datetime = moment(time, 'HH:mm:ss');
    let min = moment(datetime).format('m');
    let hour = moment(datetime).format('h');
    let partOfTheDay = moment(datetime).format('a');

    if (min == '0') {
        min = '';
    }

    return `${hour} ${min} ${partOfTheDay}`;
};

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

    console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
    console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
   
    let channelInput = request.body.queryResult.parameters.channel;
    let requestedTime = request.body.queryResult.parameters.time;
    let url = `${TVGUIDE_WEBSERVICE}/${channelInput}`;

    var intentMap = new Map();
    intentMap.set('Test Intent', testHandler);
    intentMap.set('Channel Intent', channelHandler);
    agent.handleRequest(intentMap);
});

cc84351f0d03ab6f.png

  1. 편집기에서 package.json 탭을 클릭하고 다음 JSON 코드를 복사하여 붙여넣습니다. 그러면 모든 Node.js 패키지 관리자 (NPM) 라이브러리를 가져옵니다.
{
  "name": "tvGuideFulfillment",
  "description": "Requesting TV Guide information from a web service.",
  "version": "1.0.0",
  "private": true,
  "license": "Apache Version 2.0",
  "author": "Google Inc.",
  "engines": {
    "node": "8"
  },
  "scripts": {
    "start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
    "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
  },
  "dependencies": {
    "actions-on-google": "^2.2.0",
    "firebase-admin": "^5.13.1",
    "firebase-functions": "^2.0.2",
    "request": "^2.85.0",
    "request-promise": "^4.2.5",
    "moment" : "^2.24.0",
    "dialogflow-fulfillment": "^0.6.1"
  }
}

af01460c2a023e68.png

  1. 배포 버튼을 클릭합니다. 서버리스 함수를 배포하기 때문에 잠시만 기다려 주세요. 화면 하단에 내 상태를 알려주는 팝업이 표시됩니다.
  2. 웹훅을 테스트하여 코드가 작동하는지 확인해 보겠습니다. 오른쪽의 시뮬레이터에서 다음을 입력합니다.

Test my agent.

모든 항목이 올바르면 'This is a test message'가 표시됩니다.

  1. 채널 인텐트를 테스트해 보겠습니다. 이제 다음과 같이 질문합니다.

What's on MTV?

모든 항목이 올바르면 다음과 같이 표시됩니다.

"오후 4시 30분에 MTV에서 MTV 언플러그드가 재생되고 있습니다. 이후 오후 5시 30분에 루포울스 드래그 레이스가 시작됩니다."

선택적 단계 - Firebase

다른 채널에서 테스트하면 TV 결과가 동일한 것을 알 수 있습니다. 이는 Cloud 함수가 아직 실제 웹 서버에서 가져오지 않기 때문입니다.

이렇게 하려면 아웃바운드 네트워크 연결을 설정해야 합니다.

웹 서비스로 이 애플리케이션을 테스트하려면 Firebase 요금제를 Blaze로 업그레이드하세요. 참고: 이 단계는 선택사항입니다. 이 실습의 다음 단계로 이동하여 Actions on Google에서 애플리케이션을 계속 테스트할 수도 있습니다.

  1. Firebase Console(https://console.firebase.google.com)으로 이동합니다.

  1. 화면 하단에서 업그레이드 버튼을 누릅니다.

ad38bc6d07462abf.png

팝업에서 Blaze 요금제를 선택합니다.

  1. 웹훅이 작동하는 것을 확인했으므로 계속 진행하여 index.js 코드를 아래 코드로 바꿀 수 있습니다. 이렇게 하면 웹 서비스에서 TV 가이드 정보를 요청할 수 있습니다.
'use strict';

process.env.DEBUG = 'dialogflow:debug';

const {
  dialogflow,
  BasicCard,
  Button,
  Image,
  List
 } = require('actions-on-google');

const functions = require('firebase-functions');
const moment = require('moment');
const { WebhookClient } = require('dialogflow-fulfillment');
const rp = require('request-promise');

const TVGUIDE_WEBSERVICE = 'https://tvguide-e4s5ds5dsa-ew.a.run.app/channel';
var spokenText = '';
var results = null;


/* When the Test Intent gets invoked. */
function testHandler(agent) {
    let spokenText = 'This is a test message, when you see this, it means your webhook fulfillment worked!';

    if (agent.requestSource === agent.ACTIONS_ON_GOOGLE) {
        let conv = agent.conv();
        conv.ask(spokenText);
        conv.ask(new BasicCard({
            title: `Test Message`,
            subTitle: `Dialogflow Test`,
            image: new Image({
                url: 'https://dummyimage.com/600x400/000/fff',
                alt: 'Image alternate text',
            }),
            text: spokenText,
            buttons: new Button({
                title: 'This is a button',
                url: 'https://assistant.google.com/',
            }),
        }));
        // Add Actions on Google library responses to your agent's response
        agent.add(conv);
    } else {
        agent.add(spokenText);
    }
}

/* When the Channel Intent gets invoked. */
function channelHandler(agent) {
    var listItems = {};
    spokenText = getSpeech(results);

    for (var i = 0; i < results['Listings'].length; i++) {
        listItems[`SELECT_${i}`] = {
            title: `${getSpokenTime(results['Listings'][i]['Time'])} - ${results['Listings'][i]['Title']}`,
            description: `Channel: ${results['Name']}`

        }
    }
    if (agent.requestSource === agent.ACTIONS_ON_GOOGLE) {
        let conv = agent.conv();
        conv.ask(spokenText);
        conv.ask(new List({
            title: 'TV Guide',
            items: listItems
        }));
        // Add Actions on Google library responses to your agent's response
        agent.add(conv);
    } else {
        agent.add(spokenText);
    }
}

/**
 * Return a text string to be spoken out by the Google Assistant
 * @param {object} JSON tv results
 */
var getSpeech = function(tvresults) {
    let s = "";
    if(tvresults && tvresults['Listings'][0]) {
        let channelName = tvresults['Name'];
        let currentlyPlayingTime = getSpokenTime(tvresults['Listings'][0]['Time']);
        let laterPlayingTime = getSpokenTime(tvresults['Listings'][1]['Time']);
        s = `On ${channelName} from ${currentlyPlayingTime}, ${tvresults['Listings'][0]['Title']} is playing.
        Afterwards at ${laterPlayingTime}, ${tvresults['Listings'][1]['Title']} will start.`
    }

    return s;
}

/**
 * Return a natural spoken time
 * @param {string} time in 'HH:mm:ss' format
 * @returns {string} spoken time (like 8 30 pm i.s.o. 20:00:00)
 */
var getSpokenTime = function(time){
    let datetime = moment(time, 'HH:mm:ss');
    let min = moment(datetime).format('m');
    let hour = moment(datetime).format('h');
    let partOfTheDay = moment(datetime).format('a');

    if (min == '0') {
        min = '';
    }

    return `${hour} ${min} ${partOfTheDay}`;
};

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

    console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
    console.log('Dialogflow Request body: ' + JSON.stringify(request.body));
   
    let channelInput = request.body.queryResult.parameters.channel;
    let requestedTime = request.body.queryResult.parameters.time;
    let url = `${TVGUIDE_WEBSERVICE}/${channelInput}`;

    if (requestedTime) {
        console.log(requestedTime);
        let offsetMin = moment().utcOffset(requestedTime)._offset;
        console.log(offsetMin);
        let time = moment(requestedTime).utc().add(offsetMin,'m').format('HH:mm:ss');
        url = `${TVGUIDE_WEBSERVICE}/${channelInput}/${time}`;
      }
    
      console.log(url);
  
      var options = {
          uri: encodeURI(url),
          json: true
      };
       
      // request promise calls an URL and returns the JSON response.
      rp(options)
        .then(function(tvresults) {
            console.log(tvresults);
            // the JSON response, will need to be formatted in 'spoken' text strings.
            spokenText = getSpeech(tvresults);
            results = tvresults;
        })
        .catch(function (err) {
            console.error(err);
        })
        .finally(function(){
            // kick start the Dialogflow app
            // based on an intent match, execute
            var intentMap = new Map();
            intentMap.set('Test Intent', testHandler);
            intentMap.set('Channel Intent', channelHandler);
            agent.handleRequest(intentMap);
        });
});

6. Actions on Google

Actions on Google은 Google 어시스턴트용 개발 플랫폼입니다. 이를 통해 서드 파티에서 확장된 기능을 제공하는 Google 어시스턴트용 애플릿인 '작업'을 개발할 수 있습니다.

Google에 앱을 열거나 대화하도록 요청하여 Google 작업을 호출해야 합니다.

그러면 작업이 열리고 음성이 변경되며 '기본' Google 어시스턴트 범위 즉, 이 시점에서 에이전트에 요청하는 모든 항목은 개발자가 만들어야 합니다. Google 어시스턴트에 Google 날씨 정보를 원하는 경우 갑자기 물어볼 수 없습니다. 먼저 작업 범위 (앱)를 떠나야 합니다 (닫기).

Google 어시스턴트 시뮬레이터에서 작업 테스트하기

다음 대화를 테스트해 보겠습니다.

사용자

Google 어시스턴트

"Hey Google, your-name-tv-guide한테 말해 줘."

"알겠습니다. your-name-tv-guide로 이동해 보겠습니다."

사용자

내 이름 TV 가이드 상담사

-

'환영합니다. 저는 TV 가이드입니다....'

에이전트 테스트

"테스트 메시지입니다. 이 메시지가 표시되면 웹훅 처리가 작동한 것입니다."

MTV에서 뭐 해?

오후 4시 30분 MTV에서 MTV 언플러그드가 재생되고 있습니다. 이후 오후 5시 30분에 루포울스 드래그 레이스가 시작됩니다.

  1. Google 어시스턴트 시뮬레이터로 다시 전환

열기: https://console.actions.google.com

  1. 마이크 아이콘을 클릭하고 다음과 같이 질문하세요.

c3b200803c7ba95e.png

  • Talk to my test agent
  • Test my agent

Google 어시스턴트는 다음과 같이 응답해야 합니다.

5d93c6d037c8c8eb.png

  1. 이제 다음을 질문해 보겠습니다.
  • What's on Comedy Central?

이는 다음을 반환해야 합니다.

현재 오후 6시에 Comedy Central에서 심슨가족을 상영합니다. 이후 오후 7시에 Family Guy가 시작됩니다.

7. 축하합니다

Dialogflow를 사용하여 첫 번째 Google 어시스턴트 작업을 만들었습니다. 수고하셨습니다.

눈치채셨겠지만 작업은 Google 계정에 연결된 테스트 모드에서 실행되었습니다. Nest 기기 또는 iOS나 Android 휴대전화의 Google 어시스턴트 앱에 동일한 계정으로 로그인하는 경우 작업을 테스트할 수도 있습니다.

이것은 워크숍 데모입니다. 하지만 실제로 Google 어시스턴트용 애플리케이션을 빌드하는 경우 승인을 위해 작업을 제출할 수 있습니다. 자세한 내용은 이 가이드를 읽어보세요.

학습한 내용

  • Dialogflow v2로 챗봇을 만드는 방법
  • Dialogflow로 커스텀 항목을 만드는 방법
  • Dialogflow로 선형 대화를 만드는 방법
  • Dialogflow 및 Firebase Functions로 웹훅 처리를 설정하는 방법
  • Actions on Google을 통해 애플리케이션을 Google 어시스턴트로 가져오는 방법

다음 단계

이 Codelab에 만족하셨나요? 멋진 실습을 살펴보세요.

Google Chat에 코드를 통합하여 이 Codelab을 계속 진행합니다.

G Suite 및 Dialogflow로 Google Chat을 위한 TV 가이드 만들기