├── .gitattributes ├── README.md └── index.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | howLotto - The simple bot from the video 2 | ======================================== 3 | 4 | This is the source code for howBot, the simple bot we made in the video below. 5 | 6 | You can watch the video that accompanies this source code here: https://youtu.be/FuKPQJoHJ_g 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const functions = require('firebase-functions'); // Cloud Functions for Firebase library 4 | const req = require('request'); 5 | //const DialogflowApp = require('actions-on-google').DialogflowApp; // Google Assistant helper library 6 | exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => { 7 | // console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers)); 8 | console.log('Dialogflow Request body: ' + JSON.stringify(request.body)); 9 | if (request.body.result) { 10 | processV1Request(request, response); 11 | } else if (request.body.queryResult) { 12 | processV2Request(request, response); 13 | } else { 14 | console.log('Invalid Request'); 15 | return response.status(400).end('Invalid Webhook Request (expecting v1 or v2 webhook request)'); 16 | } 17 | }); 18 | /* 19 | * Function to handle v2 webhook requests from Dialogflow 20 | */ 21 | function processV2Request (request, response) { 22 | // An action is a string used to identify what needs to be done in fulfillment 23 | let action = (request.body.queryResult.action) ? request.body.queryResult.action : 'default'; 24 | // Parameters are any entites that Dialogflow has extracted from the request. 25 | let parameters = request.body.queryResult.parameters || {}; // https://dialogflow.com/docs/actions-and-parameters 26 | // Contexts are objects used to track and store conversation state 27 | let inputContexts = request.body.queryResult.contexts; // https://dialogflow.com/docs/contexts 28 | // Get the request source (Google Assistant, Slack, API, etc) 29 | let requestSource = (request.body.originalDetectIntentRequest) ? request.body.originalDetectIntentRequest.source : undefined; 30 | // Get the session ID to differentiate calls from different users 31 | let session = (request.body.session) ? request.body.session : undefined; 32 | // Create handlers for Dialogflow actions as well as a 'default' handler 33 | const actionHandlers = { 34 | 35 | // The default fallback intent has been matched, try to recover (https://dialogflow.com/docs/intents#fallback_intents) 36 | 'input.unknown': () => { 37 | // Use the Actions on Google lib to respond to Google requests; for other requests use JSON 38 | sendResponse('I didn\'t get that one! -hb'); // Send simple response to user 39 | }, 40 | // Default handler for unknown or undefined actions 41 | 'default': () => { 42 | let responseToUser = { 43 | //fulfillmentMessages: richResponsesV2, // Optional, uncomment to enable 44 | //outputContexts: [{ 'name': `${session}/contexts/weather`, 'lifespanCount': 2, 'parameters': {'city': 'Rome'} }], // Optional, uncomment to enable 45 | fulfillmentText: 'Sorry! I can\'t do that ... YET! -hb' // displayed response 46 | }; 47 | sendResponse(responseToUser); 48 | }, 49 | 50 | // The default fallback intent has been matched, try to recover (https://dialogflow.com/docs/intents#fallback_intents) 51 | 'input.choosevideo': () => { 52 | 53 | const API_KEY = ''; 54 | var res = {}; 55 | var url = "https://www.googleapis.com/youtube/v3/search?part=snippet&safeSearch=strict&maxResults=5&videoSyndicated=true&videoEmbeddable=true&q=howCode&type=video&key=" + API_KEY; 56 | req(url, function (error, response, body) { 57 | if (!error && response.statusCode == 200) { 58 | var obj = JSON.parse(body); 59 | var items = obj.items; 60 | 61 | var r = { 62 | "fulfillmentMessages": [ 63 | { 64 | "platform": "FACEBOOK", 65 | "card": { 66 | "title": items[0].snippet.title, 67 | "subtitle": items[0].snippet.description, 68 | "imageUri": items[0].snippet.thumbnails.default.url, 69 | "buttons": [ 70 | { 71 | "text": "Watch video", 72 | "postback": 'https://www.youtube.com/watch?v=' + items[0].id.videoId 73 | } 74 | ] 75 | } 76 | }, 77 | ] 78 | } 79 | 80 | if (request.body.originalDetectIntentRequest.payload.source == "twitter") { 81 | sendResponse("Here's an awesome video! " + "https://www.youtube.com/watch?v=" + items[randVidId].id.videoId + " -hb"); 82 | } else { 83 | sendResponse(r); // Send simple response to user 84 | } 85 | 86 | }}); 87 | } 88 | }; 89 | // If undefined or unknown action use the default handler 90 | if (!actionHandlers[action]) { 91 | action = 'default'; 92 | } 93 | // Run the proper handler function to handle the request from Dialogflow 94 | actionHandlers[action](); 95 | // Function to send correctly formatted responses to Dialogflow which are then sent to the user 96 | function sendResponse (responseToUser) { 97 | // if the response is a string send it as a response to the user 98 | if (typeof responseToUser === 'string') { 99 | let responseJson = {fulfillmentText: responseToUser}; // displayed response 100 | response.json(responseJson); // Send response to Dialogflow 101 | } else { 102 | // If the response to the user includes rich responses or contexts send them to Dialogflow 103 | let responseJson = {}; 104 | // Define the text response 105 | responseJson.fulfillmentText = responseToUser.fulfillmentText; 106 | // Optional: add rich messages for integrations (https://dialogflow.com/docs/rich-messages) 107 | if (responseToUser.fulfillmentMessages) { 108 | responseJson.fulfillmentMessages = responseToUser.fulfillmentMessages; 109 | } 110 | // Optional: add contexts (https://dialogflow.com/docs/contexts) 111 | if (responseToUser.outputContexts) { 112 | responseJson.outputContexts = responseToUser.outputContexts; 113 | } 114 | // Send the response to Dialogflow 115 | console.log('Response to Dialogflow: ' + JSON.stringify(responseJson)); 116 | response.json(responseJson); 117 | } 118 | } 119 | } 120 | const richResponseV2Card = { 121 | 'title': 'Title: this is a title', 122 | 'subtitle': 'This is an subtitle. Text can include unicode characters including emoji 📱.', 123 | 'imageUri': 'https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png', 124 | 'buttons': [ 125 | { 126 | 'text': 'This is a button', 127 | 'postback': 'https://assistant.google.com/' 128 | } 129 | ] 130 | }; 131 | const richResponsesV2 = [ 132 | { 133 | 'platform': 'ACTIONS_ON_GOOGLE', 134 | 'simple_responses': { 135 | 'simple_responses': [ 136 | { 137 | 'text_to_speech': 'Spoken simple response', 138 | 'display_text': 'Displayed simple response' 139 | } 140 | ] 141 | } 142 | }, 143 | { 144 | 'platform': 'ACTIONS_ON_GOOGLE', 145 | 'basic_card': { 146 | 'title': 'Title: this is a title', 147 | 'subtitle': 'This is an subtitle.', 148 | 'formatted_text': 'Body text can include unicode characters including emoji 📱.', 149 | 'image': { 150 | 'image_uri': 'https://developers.google.com/actions/images/badges/XPM_BADGING_GoogleAssistant_VER.png' 151 | }, 152 | 'buttons': [ 153 | { 154 | 'title': 'This is a button', 155 | 'open_uri_action': { 156 | 'uri': 'https://assistant.google.com/' 157 | } 158 | } 159 | ] 160 | } 161 | }, 162 | { 163 | 'platform': 'FACEBOOK', 164 | 'card': richResponseV2Card 165 | }, 166 | { 167 | 'platform': 'SLACK', 168 | 'card': richResponseV2Card 169 | } 170 | ]; 171 | --------------------------------------------------------------------------------