Last active
January 31, 2020 15:49
-
-
Save pauleeeeee/0728872097e6b93a08c2bb42eac67102 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as functions from 'firebase-functions'; | |
import { google } from 'googleapis'; | |
const OAuth2 = google.auth.OAuth2; | |
import GoogleAssistant = require("google-assistant"); | |
//these are the app's credentials; the client_id and secret | |
const googleAssistantCredentials = require('../src/googleAssistantCredentials.json'); | |
//parsing library | |
const html2text = require('html-to-text'); | |
//cloud function export | |
export const googleAssistant = functions.https.onRequest(async(request, response)=>{ | |
//request has three fields... command:string, hyperMode:int, tokens:OAuthTokensObject | |
//create OAuth client | |
const oAuth2ClientForAssistant = new OAuth2( | |
googleAssistantCredentials.web.client_id, | |
googleAssistantCredentials.web.client_secret, | |
googleAssistantCredentials.web.redirect_uris[0] | |
); | |
//set user's refresh token as provided in request | |
oAuth2ClientForAssistant.setCredentials(request.body.tokens); | |
await oAuth2ClientForAssistant.refreshAccessToken().then(onfulfilled=>{oAuth2ClientForAssistant.setCredentials(onfulfilled.credentials)}).catch(error => console.log(error)) | |
//make assistant object | |
const assistant: GoogleAssistant = new GoogleAssistant({oauth2Client:oAuth2ClientForAssistant}); | |
const conversationConfig = { | |
textQuery: request.body.command, // if this is set, audio input is ignored | |
// isNew: true, // set this to true if you want to force a new conversation and ignore the old state | |
screen: { | |
isOn: false | |
} | |
} | |
//if user has HYPER mode enabled on Pebble, set screen.isOn flag to true; this results in different repsonses from GA | |
if(request.body.hyperMode == 1){ | |
conversationConfig.screen.isOn = true; | |
} | |
// configures the conversation with the assistant | |
const startConversation = (conversation:any) => { | |
//holder string to append different responses to | |
var responseText:string = ''; | |
//behavior of the assistant | |
conversation | |
.on('response', (text:string) => { | |
if (conversationConfig.screen.isOn === false) { | |
//append text to holder string | |
responseText += text; | |
} | |
}) | |
.on('screen-data', async (screen:{format:string, data:Buffer}) => { | |
if (conversationConfig.screen.isOn === true) { | |
//decode html data buffer | |
var html = await screen.data.toString('UTF-8'); | |
//strip all html from response | |
var cfg = { | |
wordwrap: false, | |
ignoreHref: true, | |
ignoreImage: true, | |
preserveNewlines: true | |
}; | |
var text = await html2text.fromString(html, cfg); | |
//append text to holder string | |
responseText += text; | |
} | |
}) | |
.on('ended', (error:any, continueConversation:boolean) => { | |
if (error){ | |
console.log('Conversation Ended Error:', error); | |
responseText += response.status(200).send({responseText:'Error: ' + JSON.stringify(error)}); | |
} | |
// example responseText you might get = "Will there be any more Star Wars movies?Disney isn't planning to release another Star Wars movie until 2022. Executives have publicly recognized the audience fatigue the current annual release schedule brings. It's the first time in four years that there won't be an annual Star Wars movie, but the franchise isn't really going anywhere.Source: The Mandalorian shows what Star Wars' future looks like over ...https://www.theverge.com/2019/12/27/21035823/mandalorian-finale-star-wars-rise-of-skywalker-baby-yoda-disney-plus-marvel theverge.com Try saying… Star Wars chronological order?2020 Movie Releases?What's after the rise of Skywalker?"; | |
//console.log(responseText); | |
//first, look for and then separate out source: | |
var sourceTextArray = responseText.split(/(Source:)/); | |
if (sourceTextArray.length > 1) { | |
//strip URLs from source text and reattach | |
const regex = /(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[-A-Z0-9+&@#\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&@#\/%=~_|$?!:,.]*\)|[A-Z0-9+&@#\/%=~_|$])/igm; | |
responseText = sourceTextArray[0] + " \n Source: " + sourceTextArray[2].replace(regex, " "); | |
}; | |
//instantiate actionArray | |
var actionArray = ['']; | |
// Look for the phrase "Try saying…" and split the responseText string | |
var groupArray = responseText.split(/(Try saying…)/); | |
// If "Try saying…" token was found, handle the text before the token as the response and the text after the token as action items that will be sent to the client to pre-popualte the "actions" section of the Pebble's Google Assistant app UI. These items will be sent as an array. | |
if (groupArray.length > 1) { | |
// handle response text, splitting at camelCase instances including situations like.This but not situations LIKE THIS. | |
responseText = groupArray[0]; | |
var textArray = groupArray[0].split(/(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[^A-z\s\d][\\\^]?)(?=[A-Z])/); | |
if (textArray.length > 1) { | |
// If splitting occurred, insert newline characters which will be handily parsed by the Pebble's C compiler | |
responseText = textArray.join(" \n "); | |
} | |
//split the text which comes after "Try saying…" with the same regex as before. This time look for numbers too. We will keep them in this array and send it back to the Pebble client | |
actionArray = groupArray[2].split(/(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[^A-z\s\d][\\\^]?)(?=[A-Z0-9])/); | |
//trim whitespace from any items in the action array | |
for (let i = 0; i < actionArray.length; i++){ | |
actionArray[i].trim(); | |
} | |
} else { | |
// Try saying… token was not found so just insert newlines where appropriate | |
var splitText = responseText.split(/(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[^A-z\s\d][\\\^]?)(?=[A-Z0-9])/); | |
if (splitText.length > 1){ | |
responseText = splitText.join(" \n "); | |
} | |
} | |
//build response object | |
var responseObject:{ | |
responseText: string, | |
actionArray?: Array<string> | |
} = { | |
responseText:responseText, | |
actionArray: actionArray | |
}; | |
response.status(200).send({responseText:responseText}); | |
}) | |
.on('error', (error:any) => { | |
// if there is an assistant error... send it to Pebble | |
// console.error(error); | |
response.status(200).send({responseText: "Error: " + JSON.stringify(error)}); | |
}); | |
}; | |
//starts the Google Assistant | |
assistant.start(conversationConfig, startConversation); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment