Skip to content

Instantly share code, notes, and snippets.

@pauleeeeee
Last active January 31, 2020 15:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pauleeeeee/0728872097e6b93a08c2bb42eac67102 to your computer and use it in GitHub Desktop.
Save pauleeeeee/0728872097e6b93a08c2bb42eac67102 to your computer and use it in GitHub Desktop.
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