Skip to content

Commit

Permalink
Merge pull request microsoft#87 from southworkscom/node-directline-we…
Browse files Browse the repository at this point in the history
…bsockets

[Node] DirectLine + WebSockets sample
  • Loading branch information
dandriscoll authored Apr 5, 2017
2 parents 52a33a8 + ce5f85c commit e4e270a
Show file tree
Hide file tree
Showing 9 changed files with 672 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Node/core-DirectLineWebSockets/DirectLineBot/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Bot Framework Credentials

MICROSOFT_APP_ID=
MICROSOFT_APP_PASSWORD=
71 changes: 71 additions & 0 deletions Node/core-DirectLineWebSockets/DirectLineBot/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// This loads the environment variables from the .env file
require('dotenv-extended').load();

var builder = require('botbuilder');
var restify = require('restify');

// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});

// Create connector and listen for messages
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
server.post('/api/messages', connector.listen());

var instructions = 'Welcome to the Bot to showcase the DirectLine API. Send \'Show me a hero card\' or \'Send me a BotFramework image\' to see how the DirectLine client supports custom channel data. Any other message will be echoed.';

var bot = new builder.UniversalBot(connector, function (session) {

var reply = new builder.Message()
.address(session.message.address);

var text = session.message.text.toLocaleLowerCase();

console.log('[' + session.message.address.conversation.id + '] Message received: ' + text);

switch (text) {
case 'show me a hero card':
reply.text('Sample message with a HeroCard attachment')
.addAttachment(new builder.HeroCard(session)
.title('Sample Hero Card')
.text('Displayed in the DirectLine client'));
break;

case 'send me a botframework image':
reply.text('Sample message with an Image attachment')
.addAttachment({
contentUrl: 'https://docs.botframework.com/en-us/images/faq-overview/botframework_overview_july.png',
contentType: 'image/png',
name: 'BotFrameworkOverview.png'
});

break;

default:
reply.text('You said \'' + session.message.text + '\'');
break;
}

session.send(reply);

});


bot.on('conversationUpdate', function (activity) {
// when user joins conversation, send instructions
if (activity.membersAdded) {
activity.membersAdded.forEach(function (identity) {
if (identity.id === activity.address.bot.id) {
var reply = new builder.Message()
.address(activity.address)
.text(instructions);
bot.send(reply);
}
});
}
});
28 changes: 28 additions & 0 deletions Node/core-DirectLineWebSockets/DirectLineBot/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "botbuilder-sample-directline-bot",
"version": "1.0.0",
"description": "Bot Builder Sample - DirectLine Sample - Bot",
"scripts": {
"start": "node app.js"
},
"author": "Microsoft Corp.",
"license": "MIT",
"keywords": [
"botbuilder",
"bots",
"chatbots",
"botbuilder-samples"
],
"bugs": {
"url": "https://github.com/Microsoft/BotBuilder-Samples/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/BotBuilder-Samples.git"
},
"dependencies": {
"botbuilder": "^3.7.0",
"dotenv-extended": "^1.0.4",
"restify": "^4.3.0"
}
}
211 changes: 211 additions & 0 deletions Node/core-DirectLineWebSockets/DirectLineClient/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
var Swagger = require('swagger-client');
var open = require('open');
var rp = require('request-promise');

// Config settings
var directLineSecret = 'DIRECTLINE_SECRET';

// directLineUserId is the field that identifies which user is sending activities to the Direct Line service.
// Because this value is created and sent within your Direct Line client, your bot should not
// trust the value for any security-sensitive operations. Instead, have the user log in and
// store any sign-in tokens against the Conversation or Private state fields. Those fields
// are secured by the conversation ID, which is protected with a signature.
var directLineUserId = 'DirectLineClient';

var useW3CWebSocket = false;
process.argv.forEach(function (val, index, array) {
if (val === 'w3c') {
useW3CWebSocket = true;
}
});

var directLineSpecUrl = 'https://docs.botframework.com/en-us/restapi/directline3/swagger.json';
var directLineClient = rp(directLineSpecUrl)
.then(function (spec) {
// Client
return new Swagger({
spec: JSON.parse(spec.trim()),
usePromise: true
});
})
.then(function (client) {
// Obtain a token using the Direct Line secret
// First, add the Direct Line Secret to the client's auth header
client.clientAuthorizations.add('AuthorizationBotConnector', new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' + directLineSecret, 'header'));

// Second, request a token for a new conversation
return client.Tokens.Tokens_GenerateTokenForNewConversation().then(function (response) {
// Then, replace the client's auth secret with the new token
var token = response.obj.token;
client.clientAuthorizations.add('AuthorizationBotConnector', new Swagger.ApiKeyAuthorization('Authorization', 'Bearer ' + token, 'header'));
return client;
});
})
.catch(function (err) {
console.error('Error initializing DirectLine client', err);
});

// Once the client is ready, create a new conversation
directLineClient.then(function (client) {
client.Conversations.Conversations_StartConversation()
.then(function (response) {
var responseObj = response.obj;

// Start console input loop from stdin
sendMessagesFromConsole(client, responseObj.conversationId);

if (useW3CWebSocket) {
// Start receiving messages from WS stream - using W3C client
startReceivingW3CWebSocketClient(responseObj.streamUrl, responseObj.conversationId);
} else {
// Start receiving messages from WS stream - using Node client
startReceivingWebSocketClient(responseObj.streamUrl, responseObj.conversationId);
}
});
});

// Read from console (stdin) and send input to conversation using DirectLine client
function sendMessagesFromConsole(client, conversationId) {
var stdin = process.openStdin();
process.stdout.write('Command> ');
stdin.addListener('data', function (e) {
var input = e.toString().trim();
if (input) {
if (input.toLowerCase() === 'exit') {
return process.exit();
}

// Send message
client.Conversations.Conversations_PostActivity(
{
conversationId: conversationId,
activity: {
textFormat: 'plain',
text: input,
type: 'message',
from: {
id: directLineUserId,
name: directLineUserId
}
}
}).catch(function (err) {
console.error('Error sending message:', err);
});

process.stdout.write('Command> ');
}
});
}

function startReceivingWebSocketClient(streamUrl, conversationId) {
console.log('Starting WebSocket Client for message streaming on conversationId: ' + conversationId);

var ws = new (require('websocket').client)();

ws.on('connectFailed', function (error) {
console.log('Connect Error: ' + error.toString());
});

ws.on('connect', function (connection) {
console.log('WebSocket Client Connected');
connection.on('error', function (error) {
console.log("Connection Error: " + error.toString());
});
connection.on('close', function () {
console.log('WebSocket Client Disconnected');
});
connection.on('message', function (message) {
// Occasionally, the Direct Line service sends an empty message as a liveness ping
// Ignore these messages
if (message.type === 'utf8' && message.utf8Data.length > 0) {
var data = JSON.parse(message.utf8Data);
printMessages(data.activities);
// var watermark = data.watermark;
}
});
});

ws.connect(streamUrl);
}

function startReceivingW3CWebSocketClient(streamUrl, conversationId) {
console.log('Starting W3C WebSocket Client for message streaming on conversationId: ' + conversationId);

var ws = new (require('websocket').w3cwebsocket)(streamUrl);

ws.onerror = function () {
console.log('Connection Error');
};

ws.onopen = function () {
console.log('W3C WebSocket Client Connected');
};

ws.onclose = function () {
console.log('W3C WebSocket Client Disconnected');
};

ws.onmessage = function (e) {
// Occasionally, the Direct Line service sends an empty message as a liveness ping
// Ignore these messages
if (typeof e.data === 'string' && e.data.length > 0) {
var data = JSON.parse(e.data);
printMessages(data.activities);
// var watermark = data.watermark;
}
};
}

// Helpers methods
function printMessages(activities) {
if (activities && activities.length) {
// Ignore own messages
activities = activities.filter(function (m) { return m.from.id !== directLineUserId });

if (activities.length) {
process.stdout.clearLine();
process.stdout.cursorTo(0);

// Print other messages
activities.forEach(printMessage);

process.stdout.write('Command> ');
}
}
}

function printMessage(activity) {
if (activity.text) {
console.log(activity.text);
}

if (activity.attachments) {
activity.attachments.forEach(function (attachment) {
switch (attachment.contentType) {
case "application/vnd.microsoft.card.hero":
renderHeroCard(attachment);
break;

case "image/png":
console.log('Opening the requested image ' + attachment.contentUrl);
open(attachment.contentUrl);
break;
}
});
}
}

function renderHeroCard(attachment) {
var width = 70;
var contentLine = function (content) {
return ' '.repeat((width - content.length) / 2) +
content +
' '.repeat((width - content.length) / 2);
}

console.log('/' + '*'.repeat(width + 1));
console.log('*' + contentLine(attachment.content.title) + '*');
console.log('*' + ' '.repeat(width) + '*');
console.log('*' + contentLine(attachment.content.text) + '*');
console.log('*'.repeat(width + 1) + '/');
}
30 changes: 30 additions & 0 deletions Node/core-DirectLineWebSockets/DirectLineClient/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "botbuilder-sample-directline-client",
"version": "1.0.0",
"description": "Bot Builder Sample - DirectLine Sample - Client",
"scripts": {
"start": "node app.js"
},
"author": "Microsoft Corp.",
"license": "MIT",
"keywords": [
"botbuilder",
"bots",
"chatbots",
"botbuilder-samples"
],
"bugs": {
"url": "https://github.com/Microsoft/BotBuilder-Samples/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/BotBuilder-Samples.git"
},
"dependencies": {
"open": "0.0.5",
"request": "^2.79.0",
"request-promise": "^4.1.1",
"swagger-client": "^2.1.18",
"websocket": "^1.0.24"
}
}
Loading

0 comments on commit e4e270a

Please sign in to comment.