Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ This code is enough to operate bots for all supported platforms. Claudia Bot Bui
* Line
* Kik
* GroupMe
* Google Assistant
* Cortana

## Creating bots

Expand Down
8 changes: 5 additions & 3 deletions lib/alexa/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ module.exports = function alexaParse(messageObject) {
sender: messageObject.session.user.userId,
text: getSlotValues(messageObject.request.intent.slots) || '',
originalRequest: messageObject,
type: 'alexa-skill'
type: 'alexa-skill',
accessToken: messageObject.session.user.accessToken
};
}

Expand All @@ -21,7 +22,8 @@ module.exports = function alexaParse(messageObject) {
sender: messageObject.session.user.userId,
text: '',
originalRequest: messageObject,
type: 'alexa-skill'
type: 'alexa-skill',
accessToken: messageObject.session.user.accessToken
};
}
};
};
8 changes: 8 additions & 0 deletions lib/bot-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const groupmeSetup = require('./groupme/setup');
const lineSetup = require('./line/setup');
const viberSetup = require('./viber/setup');
const alexaSetup = require('./alexa/setup');
const googleSetup = require('./google/setup');
const cortanaSetup = require('./cortana/setup');
const fbTemplate = require('./facebook/format-message');
const slackTemplate = require('./slack/format-message');
const slackDialog = require('./slack/format-dialog');
Expand Down Expand Up @@ -68,6 +70,12 @@ module.exports = function botBuilder(messageHandler, options, optionalLogError)
if (isEnabled('alexa')) {
alexaSetup(api, messageHandlerPromise, logError);
}
if (isEnabled('google')) {
googleSetup(api, messageHandlerPromise, logError);
}
if (isEnabled('cortana')) {
cortanaSetup(api, messageHandlerPromise, logError);
}

return api;
};
Expand Down
17 changes: 17 additions & 0 deletions lib/cortana/parse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';

module.exports = function cortanaParse(session, request) {
if (session) {
const entities = session.message.entities || [];
const authToken = entities.find((entity) => {
return entity.type === 'AuthorizationToken';
}) || {};
return {
sender: session.message.user.id,
text: session.message.text || '',
originalRequest: request,
type: 'cortana-skill',
accessToken: authToken.token
};
}
};
13 changes: 13 additions & 0 deletions lib/cortana/reply.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict';

module.exports = function cortanaReply(botResponse, session) {
if (typeof botResponse === 'string') {
session.endConversation(botResponse);
} else if (Array.isArray(botResponse)) {
for(let message of botResponse) {
session.send(message);
}
} else {
session.send(botResponse);
}
};
104 changes: 104 additions & 0 deletions lib/cortana/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
'use strict';

const prompt = require('souffleur');
const cortanaParse = require('./parse');
const cortanaReply = require('./reply');
const color = require('../console-colors');
const builder = require('botbuilder');
const envUtils = require('../utils/env-utils');

let connector = null;
let universalBot = null;

let self = this;
self.chatConnectorRequestListener = null;
self.promiseResolver = null;

module.exports = function cortanaSetup(api, bot, logError, optionalParser, optionalResponder) {
let parser = optionalParser || cortanaParse;
let responder = optionalResponder || cortanaReply;

api.post('/cortana', (request) => {
if (connector == null) {
connector = new builder.ChatConnector({
appId: envUtils.decode(request.env.cortanaAppId),
appPassword: envUtils.decode(request.env.cortanaAppPassword)
});

universalBot = new builder.UniversalBot(connector, (session) => {
let message = parser(session, request);
bot(message, request)
.then(botReply => {
responder(botReply, session);
})
.catch(logError);
});

let oldSend = universalBot.send;
universalBot.send = function(messages, done) {
let newDone = function () {
done();
self.promiseResolver();
};
oldSend.call(universalBot, messages, newDone);
};

universalBot.use({
receive: function(event, next) {
if(event.type === 'conversationUpdate') {
self.promiseResolver();
}
next();
},
send: function(event, next) {
next();
}
});

self.chatConnectorRequestListener = connector.listen();
}

let promise = new Promise((resolve) => {
self.promiseResolver = resolve;
let reqWrapper = {
body: request.body,
headers: request.headers
};
let resWrapper = {
status: () => {
},
end: () => {
}
};
self.chatConnectorRequestListener(reqWrapper, resWrapper);
});
return promise;
});

api.addPostDeployStep('cortana', (options, lambdaDetails, utils) => {
return Promise.resolve().then(() => {
if (options['configure-cortana-skill']) {
console.log(`\n\n${color.green}Cortana skill command setup${color.reset}\n`);
console.log(`\nConfigure your Cortana Skill endpoint to HTTPS and set this URL:.\n`);
console.log(`\n${color.cyan}${lambdaDetails.apiUrl}/cortana${color.reset}\n`);
console.log(`\nIn the SSL Certificate step, select "${color.dim}My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority${color.reset}".\n`);

return prompt(['appId', 'appPassword'])
.then(results => {
const deployment = {
restApiId: lambdaDetails.apiId,
stageName: lambdaDetails.alias,
variables: {
cortanaAppId: envUtils.encode(results['appId']),
cortanaAppPassword: envUtils.encode(results['appPassword'])
}
};
console.log(`\n`);

return utils.apiGatewayPromise.createDeploymentPromise(deployment);
}).catch(logError);
}
}).then(() => `${lambdaDetails.apiUrl}/cortana`)
.catch(logError);
});
};
22 changes: 22 additions & 0 deletions lib/google/assistant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const { ActionsSdkApp } = require('actions-on-google');
const expressMockery = require('node-mocks-http');

function getAssistant(request) {

// Prep the request and response.
var mockRequest = expressMockery.createRequest({
body: request.body
});

var mockResponse = expressMockery.createResponse();

// We need this monkey patch because node-mocks-http doesn't have the append.
mockResponse['append'] = (header, value) => {
console.log('Google SDK added a header: "' + header + '": "' + value + '"');
};

// Feed the request/response to the assistant SDK
return new ActionsSdkApp({ request: mockRequest, response: mockResponse });
}

module.exports = getAssistant;
13 changes: 13 additions & 0 deletions lib/google/parse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict';

module.exports = function googleParse(assistant, request) {
if (assistant) {
return {
sender: assistant.getUser().userId,
text: assistant.getRawInput(),
originalRequest: request,
type: 'google-action',
accessToken: assistant.getUser().accessToken
};
}
};
9 changes: 9 additions & 0 deletions lib/google/reply.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

module.exports = function googleReply(botResponse, botName, assistant) {
if (typeof botResponse === 'string') {
assistant.tell(botResponse);
return assistant.response_._getData();
}
return botResponse;
};
50 changes: 50 additions & 0 deletions lib/google/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict';

const prompt = require('souffleur');
const googleParse = require('./parse');
const googleReply = require('./reply');
const color = require('../console-colors');
const envUtils = require('../utils/env-utils');
const getAssistant = require('./assistant');

module.exports = function googleSetup(api, bot, logError, optionalParser, optionalResponder) {
let parser = optionalParser || googleParse;
let responder = optionalResponder || googleReply;

api.post('/google', (request) => {
const assistant = getAssistant(request);
return bot(parser(assistant, request), request)
.then(botReply => {
return responder(botReply, envUtils.decode(request.env.googleAppName), assistant);
})
.catch(logError);
});

api.addPostDeployStep('google', (options, lambdaDetails, utils) => {
return Promise.resolve().then(() => {
if (options['configure-google-action']) {
console.log(`\n\n${color.green}Google Action command setup${color.reset}\n`);
console.log(`\nConfigure your Google Action endpoint to HTTPS and set this URL:.\n`);
console.log(`\n${color.cyan}${lambdaDetails.apiUrl}/google${color.reset}\n`);
console.log(`\nIn the SSL Certificate step, select "${color.dim}My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority${color.reset}".\n`);

return prompt(['Google App Name'])
.then(results => {
const deployment = {
restApiId: lambdaDetails.apiId,
stageName: lambdaDetails.alias,
variables: {
googleAppName: envUtils.encode(results['Google App Name'])
}
};

console.log(`\n`);

return utils.apiGatewayPromise.createDeploymentPromise(deployment);
})
.catch(logError);
}
})
.then(() => `${lambdaDetails.apiUrl}/google`).catch(logError);
});
};
2 changes: 2 additions & 0 deletions lib/utils/env-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

module.exports = {
encode(str) {
if(!str) return '';
return new Buffer(str).toString('base64').replace(/\+/g, '-');
},
decode(str) {
if(!str) return '';
return new Buffer(str.replace(/\-/g, '+'), 'base64').toString('utf8');
}
};
Loading