From 3903029191b1bd3d7eaa665c0f82546a629064d2 Mon Sep 17 00:00:00 2001 From: Steven Ickman Date: Thu, 22 Sep 2016 13:33:27 -0700 Subject: [PATCH 1/5] Added BotFramework connector --- .vscode/launch.json | 46 ++++++++++ botframework_bot.js | 209 ++++++++++++++++++++++++++++++++++++++++++++ lib/BotFramework.js | 191 ++++++++++++++++++++++++++++++++++++++++ lib/Botkit.js | 2 + package.json | 1 + 5 files changed, 449 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 botframework_bot.js create mode 100644 lib/BotFramework.js diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..21ff6bb99 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,46 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch", + "type": "node", + "request": "launch", + "program": "${workspaceRoot}/botframework_bot.js", + "stopOnEntry": false, + "args": [], + "cwd": "${workspaceRoot}", + "preLaunchTask": null, + "runtimeExecutable": null, + "runtimeArgs": [ + "--nolazy" + ], + "env": { + "NODE_ENV": "development" + }, + "console": "internalConsole", + "sourceMaps": false, + "outDir": null + }, + { + "name": "Attach", + "type": "node", + "request": "attach", + "port": 5858, + "address": "localhost", + "restart": false, + "sourceMaps": false, + "outDir": null, + "localRoot": "${workspaceRoot}", + "remoteRoot": null + }, + { + "name": "Attach to Process", + "type": "node", + "request": "attach", + "processId": "${command.PickProcess}", + "port": 5858, + "sourceMaps": false, + "outDir": null + } + ] +} \ No newline at end of file diff --git a/botframework_bot.js b/botframework_bot.js new file mode 100644 index 000000000..dd926a9e6 --- /dev/null +++ b/botframework_bot.js @@ -0,0 +1,209 @@ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ______ ______ ______ __ __ __ ______ + /\ == \ /\ __ \ /\__ _\ /\ \/ / /\ \ /\__ _\ + \ \ __< \ \ \/\ \ \/_/\ \/ \ \ _"-. \ \ \ \/_/\ \/ + \ \_____\ \ \_____\ \ \_\ \ \_\ \_\ \ \_\ \ \_\ + \/_____/ \/_____/ \/_/ \/_/\/_/ \/_/ \/_/ + + +This is a sample Microsoft Bot Framework bot built with Botkit. + +This bot demonstrates many of the core features of Botkit: + +* Connect to the Microsoft Bot Framework Service +* Receive messages based on "spoken" patterns +* Reply to messages +* Use the conversation system to ask questions +* Use the built in storage system to store and retrieve information + for a user. + +# RUN THE BOT: + + Follow the instructions here to set up your Facebook app and page: + + -> https://developers.facebook.com/docs/messenger-platform/implementation + + Run your bot from the command line: + + page_token= verify_token= node facebook_bot.js [--lt [--ltsubdomain LOCALTUNNEL_SUBDOMAIN]] + + Use the --lt option to make your bot available on the web through localtunnel.me. + +# USE THE BOT: + + Find your bot inside Facebook to send it a direct message. + + Say: "Hello" + + The bot will reply "Hello!" + + Say: "who are you?" + + The bot will tell you its name, where it running, and for how long. + + Say: "Call me " + + Tell the bot your nickname. Now you are friends. + + Say: "who am I?" + + The bot will tell you your nickname, if it knows one for you. + + Say: "shutdown" + + The bot will ask if you are sure, and then shut itself down. + + Make sure to invite your bot into other channels using /invite @! + +# EXTEND THE BOT: + + Botkit has many features for building cool and useful bots! + + Read all about it here: + + -> http://howdy.ai/botkit + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + +var Botkit = require('./lib/Botkit.js'); +var os = require('os'); +var commandLineArgs = require('command-line-args'); +var localtunnel = require('localtunnel'); + +const ops = commandLineArgs([ + {name: 'lt', alias: 'l', args: 1, description: 'Use localtunnel.me to make your bot available on the web.', + type: Boolean, defaultValue: false}, + {name: 'ltsubdomain', alias: 's', args: 1, + description: 'Custom subdomain for the localtunnel.me URL. This option can only be used together with --lt.', + type: String, defaultValue: null}, + ]); + +if(ops.lt === false && ops.ltsubdomain !== null) { + console.log("error: --ltsubdomain can only be used together with --lt."); + process.exit(); +} + +var controller = Botkit.botframeworkbot({ + debug: true, + appId: process.env.app_id, + appPassword: process.env.app_password, +}); + +var bot = controller.spawn({ +}); + +controller.setupWebserver(process.env.port || 3000, function(err, webserver) { + controller.createWebhookEndpoints(webserver, bot, function() { + console.log('ONLINE!'); + if(ops.lt) { + var tunnel = localtunnel(process.env.port || 3000, {subdomain: ops.ltsubdomain}, function(err, tunnel) { + if (err) { + console.log(err); + process.exit(); + } + console.log("Your bot is available on the web at the following URL: " + tunnel.url + '/botframework/receive'); + }); + + tunnel.on('close', function() { + console.log("Your bot is no longer available on the web at the localtunnnel.me URL."); + process.exit(); + }); + } + }); +}); + + + +controller.hears(['hello', 'hi'], 'message_received', function(bot, message) { + + controller.storage.users.get(message.user, function(err, user) { + if (user && user.name) { + bot.reply(message, 'Hello ' + user.name + '!!'); + } else { + bot.reply(message, 'Hello.'); + } + }); +}); + +controller.hears(['call me (.*)'], 'message_received', function(bot, message) { + var matches = message.text.match(/call me (.*)/i); + var name = matches[1]; + controller.storage.users.get(message.user, function(err, user) { + if (!user) { + user = { + id: message.user, + }; + } + user.name = name; + controller.storage.users.save(user, function(err, id) { + bot.reply(message, 'Got it. I will call you ' + user.name + ' from now on.'); + }); + }); +}); + +controller.hears(['what is my name', 'who am i'], 'message_received', function(bot, message) { + + controller.storage.users.get(message.user, function(err, user) { + if (user && user.name) { + bot.reply(message,'Your name is ' + user.name); + } else { + bot.reply(message,'I don\'t know yet!'); + } + }); +}); + + +controller.hears(['shutdown'],'message_received',function(bot, message) { + + bot.startConversation(message,function(err, convo) { + convo.ask('Are you sure you want me to shutdown?',[ + { + pattern: bot.utterances.yes, + callback: function(response, convo) { + convo.say('Bye!'); + convo.next(); + setTimeout(function() { + process.exit(); + },3000); + } + }, + { + pattern: bot.utterances.no, + default: true, + callback: function(response, convo) { + convo.say('*Phew!*'); + convo.next(); + } + } + ]); + }); +}); + + +controller.hears(['uptime','identify yourself','who are you','what is your name'],'message_received',function(bot, message) { + + var hostname = os.hostname(); + var uptime = formatUptime(process.uptime()); + + bot.reply(message,'I am a bot! I have been running for ' + uptime + ' on ' + hostname + '.'); + +}); + +function formatUptime(uptime) { + var unit = 'second'; + if (uptime > 60) { + uptime = uptime / 60; + unit = 'minute'; + } + if (uptime > 60) { + uptime = uptime / 60; + unit = 'hour'; + } + if (uptime != 1) { + unit = unit + 's'; + } + + uptime = uptime + ' ' + unit; + return uptime; +} diff --git a/lib/BotFramework.js b/lib/BotFramework.js new file mode 100644 index 000000000..2e6aacb0e --- /dev/null +++ b/lib/BotFramework.js @@ -0,0 +1,191 @@ +var Botkit = require(__dirname + '/CoreBot.js'); +var builder = require('botbuilder'); +var express = require('express'); +var bodyParser = require('body-parser'); + +function BotFrameworkBot(configuration) { + + // Create a core botkit bot + var bf_botkit = Botkit(configuration || {}); + + // customize the bot definition, which will be used when new connections + // spawn! + bf_botkit.defineBot(function(botkit, config) { + + var bot = { + botkit: botkit, + config: config || {}, + utterances: botkit.utterances, + }; + + bot.startConversation = function(message, cb) { + botkit.startConversation(this, message, cb); + }; + + bot.send = function(message, cb) { + function done(err) { + if (cb) { + cb(err); + } + } + + if (!message || !message.address) { + if (cb) { + cb(new Error('Outgoing message requires a valid address...')); + } + return; + } + + // Copy message minus user & channel fields + var bf_message = {}; + for (var key in message) { + switch (key) { + case 'user': + case 'channel': + // ignore + break; + default: + bf_message[key] = message[key]; + break; + } + } + if (!bf_message.type) { + bf_message.type = 'message'; + } + + // Ensure the message address has a valid conversation id. + if (!bf_message.address.conversation) { + bot.connector.startConversation(bf_message.address, function (err, adr) { + if (!err) { + // Send message through connector + bf_message.address = adr; + bot.connector.send([bf_message], done); + } else { + done(err); + } + }); + } else { + // Send message through connector + bot.connector.send([bf_message], done); + } + }; + + bot.reply = function(src, resp, cb) { + var msg = {}; + + if (typeof(resp) == 'string') { + msg.text = resp; + } else { + msg = resp; + } + + msg.user = src.user; + msg.channel = src.channel; + msg.address = src.address; + + bot.say(msg, cb); + }; + + bot.findConversation = function(message, cb) { + botkit.debug('CUSTOM FIND CONVO', message.user, message.channel); + for (var t = 0; t < botkit.tasks.length; t++) { + for (var c = 0; c < botkit.tasks[t].convos.length; c++) { + if ( + botkit.tasks[t].convos[c].isActive() && + botkit.tasks[t].convos[c].source_message.user == message.user && + botkit.tasks[t].convos[c].source_message.channel == message.channel + ) { + botkit.debug('FOUND EXISTING CONVO!'); + cb(botkit.tasks[t].convos[c]); + return; + } + } + } + + cb(); + }; + + // Create connector + bot.connector = new builder.ChatConnector(config); + + return bot; + + }); + + + // set up a web route for receiving outgoing webhooks and/or slash commands + + bf_botkit.createWebhookEndpoints = function(webserver, bot, cb) { + + // Listen for incoming events + bf_botkit.log( + '** Serving webhook endpoints for the Microsoft Bot Framework at: ' + + 'http://MY_HOST:' + bf_botkit.config.port + '/botframework/receive'); + webserver.post('/botframework/receive', bot.connector.listen()); + + // Receive events from chat connector + bot.connector.onEvent(function (events, done) { + for (var i = 0; i < events.length; i++) { + // Break out user & channel fields from event + // - These fields are used as keys for tracking conversations and storage. + // - Prefixing with channelId to ensure that users & channels for different + // platforms are unique. + var bf_event = events[i]; + var prefix = bf_event.address.channelId + ':'; + bf_event.user = prefix + bf_event.address.user.id; + bf_event.channel = prefix + bf_event.address.conversation.id; + + // Dispatch event + if (bf_event.type === 'message') { + bf_botkit.receiveMessage(bot, bf_event); + } else { + bf_botkit.trigger(bf_event.type, [bot, bf_event]); + } + } + + if (done) { + done(null); + } + }); + + if (cb) { + cb(); + } + + bf_botkit.startTicking(); + + return bf_botkit; + }; + + bf_botkit.setupWebserver = function(port, cb) { + + if (!port) { + throw new Error('Cannot start webserver without a port'); + } + if (isNaN(port)) { + throw new Error('Specified port is not a valid number'); + } + + bf_botkit.config.port = port; + + bf_botkit.webserver = express(); + bf_botkit.webserver.use(bodyParser.json()); + bf_botkit.webserver.use(bodyParser.urlencoded({ extended: true })); + bf_botkit.webserver.use(express.static(__dirname + '/public')); + + var server = bf_botkit.webserver.listen( + bf_botkit.config.port, + function() { + bf_botkit.log('** Starting webserver on port ' + + bf_botkit.config.port); + if (cb) { cb(null, bf_botkit.webserver); } + }); + + return bf_botkit; + + }; + + return bf_botkit; +}; + +module.exports = BotFrameworkBot; diff --git a/lib/Botkit.js b/lib/Botkit.js index 86e44d8fe..9d754c327 100755 --- a/lib/Botkit.js +++ b/lib/Botkit.js @@ -2,10 +2,12 @@ var CoreBot = require(__dirname + '/CoreBot.js'); var Slackbot = require(__dirname + '/SlackBot.js'); var Facebookbot = require(__dirname + '/Facebook.js'); var TwilioIPMbot = require(__dirname + '/TwilioIPMBot.js'); +var BotFrameworkBot = require(__dirname + '/BotFramework.js'); module.exports = { core: CoreBot, slackbot: Slackbot, facebookbot: Facebookbot, twilioipmbot: TwilioIPMbot, + botframeworkbot: BotFrameworkBot, }; diff --git a/package.json b/package.json index eab32c777..6274f7ca5 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "async": "^2.0.0-rc.5", "back": "^1.0.1", "body-parser": "^1.14.2", + "botbuilder": "^3.2.3", "command-line-args": "^3.0.0", "express": "^4.13.3", "https-proxy-agent": "^1.0.0", From d2593a03d72bfa7e61e71ea9f17cbf99e89a4cb3 Mon Sep 17 00:00:00 2001 From: Steven Ickman Date: Thu, 22 Sep 2016 17:01:00 -0700 Subject: [PATCH 2/5] added readme for botframework --- readme-botframework.md | 310 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 readme-botframework.md diff --git a/readme-botframework.md b/readme-botframework.md new file mode 100644 index 000000000..a48f13a51 --- /dev/null +++ b/readme-botframework.md @@ -0,0 +1,310 @@ +# Botkit and Microsoft Bot Framework + +Botkit is designed to ease the process of designing and running useful, creative bots that live inside [Slack](http://slack.com), [Facebook Messenger](http://facebook.com), [Twilio IP Messaging](https://www.twilio.com/docs/api/ip-messaging), [Microsoft Bot Framework](https://botframework.com), +and other messaging platforms. + +The [Microsoft Bot Framework](https://botframework.com) makes it easy to create a single bot that can run across a variety of messaging channels including [Skype](https://skype.com), [Group.me](https://groupme.com), [Facebook Messenger](https://messenger.com), [Slack](https://slack.com), +[Telegram](https://telegram.org/), [Kik](https://www.kik.com/), [SMS](https://www.twilio.com/), and [email](https://microsoft.office.com). + +Built in to [Botkit](https://howdy.ai/botkit/) are a comprehensive set of features and tools to deal with any of the platforms supported by the [Microsoft Bot Framework](https://botframework.com), allowing developers to build interactive bots and applications that send and receive messages +just like real humans. + +This document covers the Bot Framework implementation details only. [Start here](readme.md) if you want to learn about to develop with Botkit. + +Table of Contents + +* [Getting Started](#getting-started) +* [Bot Framework Events](#twilio-ipm-specific-events) +* [Working with Twilio IPM](#working-with-twilio-ip-messaging) +* [System Bots vs User Bots](#system-bots-vs-user-bots) +* [Using Twilio's API](#using-the-twilio-api) + +## Getting Started + +1) Install Botkit [more info here](readme.md#installation) + +2) Register a developer account with the Bot Framework [Developer Portal](https://dev.botframework.com/) and follow [this guide](https://docs.botframework.com/en-us/csharp/builder/sdkreference/gettingstarted.html#registering) to register your first bot with the Bot Framework. + +* You'll be asked to provide an endpoint for your bot during the registration process which you should set to "https:///botframework/receive". If your using a service like [ngrok](https://ngrok.com/) to run your bot locally you should set the "" portion of your endpoint to + be the hostname assigned by ngrok. +* Write down the *App ID* and *App Password* assigned to your new bot as you'll need them when you run your bot. + +3) By default your bot will be configured to support the Skype channel but you'll need to add it as a contact on Skype in order to test it. You can do that from the developer portal by clicking the "Add to Skype" button in your bots profile page. + +4) Run the example bot using the App ID & Password you were assigned. If you are _not_ running your bot at a public, SSL-enabled internet address, use the --lt option and update your bots endpoint in the developer portal to use the URL assigned to your bot. + +``` +app_id= app_password= node botframework_bot.js [--lt [--ltsubdomain CUSTOM_SUBDOMAIN]] +``` + +5) Your bot should be online! Within Skype, find the bot in your contacts list, and send it a message. + +Try: + * who are you? + * call me Bob + * shutdown + +##### Twilio Account SID and Auth Token + +These values are available on your [Twilio Account page](https://www.twilio.com/user/account/settings). Copy both the SID and token values. + +##### API Key and API Secret + +To get an API key and secret [go here](https://www.twilio.com/user/account/ip-messaging/dev-tools/api-keys) and click 'Create an API Key'. Provide a friendly name for the API service and click 'Create API Key'. Be sure to copy your Twilio API key and API Secret keys to a safe location - this is the last time Twilio will show you your secret! Click the checkbox for 'Got it! I have saved my API Key Sid and Secret in a safe place to use in my application.' + +##### Service SID + +To generate a Twilio service SID, [go here](https://www.twilio.com/user/account/ip-messaging/services) and click 'Create an IP Messaging Service'. + +Provide a friendly name and click 'create'. At the top under 'Properties' you should see Service SID. Copy this to a safe place. You now have all 5 values! + +*Keep this tab open!* You'll come back here in step 7 to specify your bot's webhook endpoint URL. + +4) Now that you've got all the credentials, you need to set up an actual IP Messaging client. If you don't already have a native app built, the quickest way to get started is to clone the Twilio IPM client demo, which is available at [https://github.com/twilio/ip-messaging-demo-js](https://github.com/twilio/ip-messaging-demo-js) + +Follow the instructions to get your IP Messaging Demo client up and running using the credentials you collected above. + +5) Start up the sample Twilio IPM Bot. From inside your cloned Botkit repo, run: +``` +TWILIO_ACCOUNT_SID= TWILIO_AUTH_TOKEN= TWILIO_IPM_SERVICE_SID= TWILIO_API_KEY= TWILIO_API_SECRET= node twilio_ipm_bot.js +``` + +6) If you are _not_ running your bot at a public, SSL-enabled internet address, use [localtunnel.me](http://localtunnel.me) to make it available to Twilio. Note the URL it gives you. For example, it may say your url is `https://xyx.localtunnel.me/` In this case, the webhook URL for use in step 7 would be `https://xyx.localtunnel.me/twilio/receive` + +7) Set up a webhook endpoint for your app that uses your public URL, or the URL that localtunnel gave you. This is done on [settings page for your IP Messaging service](https://www.twilio.com/user/account/ip-messaging/services). Enable *all of the POST-event* webhooks events! + +6) Load your IP Messaging client, and talk to your bot! + +Try: + +* hello +* who am i? +* call me Bob +* shutdown + +### Things to note + +Since Twilio delivers messages via web hook, your application must be available at a public internet address. Additionally, Twilio requires this address to use SSL. Luckily, you can use [LocalTunnel](https://localtunnel.me/) to make a process running locally or in your dev environment available in a Twilio-friendly way. + +Additionally, you need to enable your Twilio IPM instance's webhook callback events. This can be done via the Twilio dashboard, but can also be done automatically using a Bash script. You can use the sample script below to enable all of the post-event webhook callbacks: + +``` +#!/bin/bash +echo 'please enter the service uri' +read servuri + +echo 'please enter the service sid' +read servsid + +echo 'please enter the account sid' +read accsid + +echo 'please enter the auth token' +read authtok + +onChannelDestroyedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnChannelDestroyed.Url=$servuri/twilio/receive' -d 'Webhooks.OnChannelDestroyed.Method=POST' -d 'Webhooks.OnChannelDestroyed.Format=XML' -u '$accsid:$authtok'" +eval $onChannelDestroyedCurl + +onChannelAddedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnChannelAdded.Url=$servuri/twilio/receive' -d 'Webhooks.OnChannelAdded.Method=POST' -d 'Webhooks.OnChannelAdded.Format=XML' -u '$accsid:$authtok'" +eval $onChannelAddedCurl + +onMemberRemovedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnMemberRemoved.Url=$servuri/twilio/receive' -d 'Webhooks.OnMemberRemoved.Method=POST' -d 'Webhooks.OnMemberRemoved.Format=XML' -u '$accsid:$authtok'" +eval $onMemberRemovedCurl +onMessageRemovedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnMessageRemoved.Url=$servuri/twilio/receive' -d 'Webhooks.OnMessageRemoved.Method=POST' -d 'Webhooks.OnMessageRemoved.Format=XML' -u '$accsid:$authtok'" +eval $onMessageRemovedCurl + +onMessageUpdatedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnMessageUpdated.Url=$servuri/twilio/receive' -d 'Webhooks.OnMessageUpdated.Method=POST' -d 'Webhooks.OnMessageUpdated.Format=XML' -u '$accsid:$authtok'" +eval $onMessageUpdatedCurl + +onChannelUpdatedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnChannelUpdated.Url=$servuri/twilio/receive' -d 'Webhooks.OnChannelUpdated.Method=POST' -d 'Webhooks.OnChannelUpdated.Format=XML' -u '$accsid:$authtok'" +eval $onChannelUpdatedCurl + +onMemberAddedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnMemberAdded.Url=$servuri/twilio/receive' -d 'Webhooks.OnMemberAdded.Method=POST' -d 'Webhooks.OnMemberAdded.Format=XML' -u '$accsid:$authtok'" +eval $onMemberAddedCurl +``` + +When you are ready to go live, consider [LetsEncrypt.org](http://letsencrypt.org), a _free_ SSL Certificate Signing Authority which can be used to secure your website very quickly. It is fabulous and we love it. + +## Twilio IPM Specific Events + +Once connected to your Twilio IPM service, bots receive a constant stream of events. + +Normal messages will be sent to your bot using the `message_received` event. In addition, Botkit will trigger these Botkit-specific events: + +| Event | Description +|--- |--- +| bot_channel_join| The bot has joined a channel +| bot_channel_leave | The bot has left a channel +| user_channel_join | A user (not the bot) has joined a channel +| user_channel_leave | A user (not the bot) has left a channel + +Botkit will handle and distribute [all of the Twilio IPM API webhooks events](https://www.twilio.com/docs/api/ip-messaging/webhooks). Your Bot can act on any of these events, and will receive the complete payload from Twilio. Below, is a list of the IPM API callback events that can be subscribed to in your Bot: + +| Event | Description +|--- |--- +| onMessageSent | Message sent +| onMessageRemoved | Message removed/deleted +| onMessageUpdated | Message edited +| onChannelAdded | Channel created +| onChannelUpdated | Channel FriendlyName or Attributes updated +| onChannelDestroyed | Channel Deleted/Destroyed +| onMemberAdded | Channel Member Joined or Added +| onMemberRemoved | Channel Member Removed or Left + + +## Working with Twilio IP Messaging + +Botkit receives messages from Twilio IPM using Webhooks, and sends messages using Twilio's REST APIs. This means that your Bot application must present a web server that is publicly addressable. Everything you need to get started is already included in Botkit. + +To connect your bot to Twilio, [follow the instructions here](https://www.twilio.com/user/account/ip-messaging/getting-started). You will need to collect 5 separate pieces of your API credentials. A step by step guide [can be found here](#getting-started). Since you must *already be running* your Botkit app to fully configure your Twilio app, there is a bit of back-and-forth. It's ok! You can do it. + +Here is the complete code for a basic Twilio bot: + +```javascript +var Botkit = require('botkit'); +var controller = Botkit.twilioipmbot({ + debug: false +}) + +var bot = controller.spawn({ + TWILIO_IPM_SERVICE_SID: process.env.TWILIO_IPM_SERVICE_SID, + TWILIO_ACCOUNT_SID: process.env.TWILIO_ACCOUNT_SID, + TWILIO_API_KEY: process.env.TWILIO_API_KEY, + TWILIO_API_SECRET: process.env.TWILIO_API_SECRET, + identity: 'Botkit', + autojoin: true +}); + +// if you are already using Express, you can use your own server instance... +// see "Use BotKit with an Express web server" +controller.setupWebserver(process.env.port,function(err,webserver) { + controller.createWebhookEndpoints(controller.webserver, bot, function() { + console.log('This bot is online!!!'); + }); +}); + +// user said hello +controller.hears(['hello'], 'message_received', function(bot, message) { + + bot.reply(message, 'Hey there.'); + +}); + +controller.hears(['cookies'], 'message_received', function(bot, message) { + + bot.startConversation(message, function(err, convo) { + + convo.say('Did someone say cookies!?!!'); + convo.ask('What is your favorite type of cookie?', function(response, convo) { + convo.say('Golly, I love ' + response.text + ' too!!!'); + convo.next(); + }); + }); +}); +``` + + +#### controller.setupWebserver() +| Argument | Description +|--- |--- +| port | port for webserver +| callback | callback function + +Setup an [Express webserver](http://expressjs.com/en/index.html) for +use with `createWebhookEndpoints()` + +If you need more than a simple webserver to receive webhooks, +you should by all means create your own Express webserver! + +The callback function receives the Express object as a parameter, +which may be used to add further web server routes. + +#### controller.createWebhookEndpoints() + +This function configures the route `https://_your_server_/twilio/receive` +to receive webhooks from twilio. + +This url should be used when configuring Twilio. + +## System Bots vs User Bots + +Bots inside a Twilio IPM environment can run in one of two ways: as the "system" user, +ever present and automatically available in all channels, OR, as a specific "bot" user +who must be added to channels in order to interact. + +By default, bots are "system" users, and can be configured as below: + +``` +var bot = controller.spawn({ + TWILIO_IPM_SERVICE_SID: process.env.TWILIO_IPM_SERVICE_SID, + TWILIO_ACCOUNT_SID: process.env.TWILIO_ACCOUNT_SID, + TWILIO_API_KEY: process.env.TWILIO_API_KEY, + TWILIO_API_SECRET: process.env.TWILIO_API_SECRET, +}); +``` + +To connect as a "bot" user, pass in an `identity` field: + +``` +var bot = controller.spawn({ + TWILIO_IPM_SERVICE_SID: process.env.TWILIO_IPM_SERVICE_SID, + TWILIO_ACCOUNT_SID: process.env.TWILIO_ACCOUNT_SID, + TWILIO_API_KEY: process.env.TWILIO_API_KEY, + TWILIO_API_SECRET: process.env.TWILIO_API_SECRET, + identity: 'My Bot Name', +}); +``` + +To have your bot automatically join every channel as they are created and removed, +pass in `autojoin`: + +``` +var bot = controller.spawn({ + TWILIO_IPM_SERVICE_SID: process.env.TWILIO_IPM_SERVICE_SID, + TWILIO_ACCOUNT_SID: process.env.TWILIO_ACCOUNT_SID, + TWILIO_API_KEY: process.env.TWILIO_API_KEY, + TWILIO_API_SECRET: process.env.TWILIO_API_SECRET, + identity: 'Botkit', + autojoin: true +}); +``` + +## Using the Twilio API + +You can use the Twilio API directly in your Bot via Botkit's bot.api object. Botkit's bot.api provides a thin wrapper on the [Twilio official module](http://twilio.github.io/twilio-node/). + +For example, to [retrieve a member from a channel](https://www.twilio.com/docs/api/ip-messaging/rest/members#action-get) using the un-wrapped Twilio API client, you would use the following code: + +```javascript +service.channels('CHANNEL_SID').members('MEMBER_SID').get().then(function(response) { + console.log(response); +}).fail(function(error) { + console.log(error); +}); +``` + +In Botkit, this can be accomplished by simply replacing the reference to a `service` object, with the `bot.api` object, as shown here: + +```javascript +bot.api.channels('CHANNEL_SID').members('MEMBER_SID').get().then(function(response) { + console.log(response); +}).fail(function(error) { + console.log(error); +}); +``` +This gives you full access to all of the Twilio API methods so that you can use them in your Bot. + +Here is an example showing how to join a channel using Botkit's bot.api object, which creates a member to the channel, by wrapping the IPM API. + +```javascript +controller.on('onChannelAdded', function(bot, message){ + // whenever a channel gets added, join it! + bot.api.channels(message.channel).members.create({ + identity: bot.identity + }).then(function(response) { + + }).fail(function(error) { + console.log(error); + }); +}); +``` From e3e24555de7efc9dafecce588b695988d85a659e Mon Sep 17 00:00:00 2001 From: Steven Ickman Date: Fri, 23 Sep 2016 15:46:05 -0700 Subject: [PATCH 3/5] updated readme --- readme-botframework.md | 237 +++++++---------------------------------- 1 file changed, 37 insertions(+), 200 deletions(-) diff --git a/readme-botframework.md b/readme-botframework.md index a48f13a51..5be664090 100644 --- a/readme-botframework.md +++ b/readme-botframework.md @@ -14,10 +14,8 @@ This document covers the Bot Framework implementation details only. [Start here] Table of Contents * [Getting Started](#getting-started) -* [Bot Framework Events](#twilio-ipm-specific-events) -* [Working with Twilio IPM](#working-with-twilio-ip-messaging) -* [System Bots vs User Bots](#system-bots-vs-user-bots) -* [Using Twilio's API](#using-the-twilio-api) +* [Bot Framework Specific Events](#bot-framework-specific-events) +* [Working with the Bot Framework IPM](#working-with-the-bot-framework) ## Getting Started @@ -44,135 +42,45 @@ Try: * call me Bob * shutdown -##### Twilio Account SID and Auth Token - -These values are available on your [Twilio Account page](https://www.twilio.com/user/account/settings). Copy both the SID and token values. - -##### API Key and API Secret - -To get an API key and secret [go here](https://www.twilio.com/user/account/ip-messaging/dev-tools/api-keys) and click 'Create an API Key'. Provide a friendly name for the API service and click 'Create API Key'. Be sure to copy your Twilio API key and API Secret keys to a safe location - this is the last time Twilio will show you your secret! Click the checkbox for 'Got it! I have saved my API Key Sid and Secret in a safe place to use in my application.' - -##### Service SID - -To generate a Twilio service SID, [go here](https://www.twilio.com/user/account/ip-messaging/services) and click 'Create an IP Messaging Service'. - -Provide a friendly name and click 'create'. At the top under 'Properties' you should see Service SID. Copy this to a safe place. You now have all 5 values! - -*Keep this tab open!* You'll come back here in step 7 to specify your bot's webhook endpoint URL. - -4) Now that you've got all the credentials, you need to set up an actual IP Messaging client. If you don't already have a native app built, the quickest way to get started is to clone the Twilio IPM client demo, which is available at [https://github.com/twilio/ip-messaging-demo-js](https://github.com/twilio/ip-messaging-demo-js) - -Follow the instructions to get your IP Messaging Demo client up and running using the credentials you collected above. - -5) Start up the sample Twilio IPM Bot. From inside your cloned Botkit repo, run: -``` -TWILIO_ACCOUNT_SID= TWILIO_AUTH_TOKEN= TWILIO_IPM_SERVICE_SID= TWILIO_API_KEY= TWILIO_API_SECRET= node twilio_ipm_bot.js -``` - -6) If you are _not_ running your bot at a public, SSL-enabled internet address, use [localtunnel.me](http://localtunnel.me) to make it available to Twilio. Note the URL it gives you. For example, it may say your url is `https://xyx.localtunnel.me/` In this case, the webhook URL for use in step 7 would be `https://xyx.localtunnel.me/twilio/receive` - -7) Set up a webhook endpoint for your app that uses your public URL, or the URL that localtunnel gave you. This is done on [settings page for your IP Messaging service](https://www.twilio.com/user/account/ip-messaging/services). Enable *all of the POST-event* webhooks events! - -6) Load your IP Messaging client, and talk to your bot! - -Try: - -* hello -* who am i? -* call me Bob -* shutdown - ### Things to note -Since Twilio delivers messages via web hook, your application must be available at a public internet address. Additionally, Twilio requires this address to use SSL. Luckily, you can use [LocalTunnel](https://localtunnel.me/) to make a process running locally or in your dev environment available in a Twilio-friendly way. - -Additionally, you need to enable your Twilio IPM instance's webhook callback events. This can be done via the Twilio dashboard, but can also be done automatically using a Bash script. You can use the sample script below to enable all of the post-event webhook callbacks: +Since the Bot Framework delivers messages via web hook, your application must be available at a public internet address. Additionally, the Bot Framework requires this address to use SSL. Luckily, you can use [LocalTunnel](https://localtunnel.me/) to make a process running locally or in your dev environment available in a Bot Framework friendly way. -``` -#!/bin/bash -echo 'please enter the service uri' -read servuri - -echo 'please enter the service sid' -read servsid - -echo 'please enter the account sid' -read accsid - -echo 'please enter the auth token' -read authtok - -onChannelDestroyedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnChannelDestroyed.Url=$servuri/twilio/receive' -d 'Webhooks.OnChannelDestroyed.Method=POST' -d 'Webhooks.OnChannelDestroyed.Format=XML' -u '$accsid:$authtok'" -eval $onChannelDestroyedCurl - -onChannelAddedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnChannelAdded.Url=$servuri/twilio/receive' -d 'Webhooks.OnChannelAdded.Method=POST' -d 'Webhooks.OnChannelAdded.Format=XML' -u '$accsid:$authtok'" -eval $onChannelAddedCurl - -onMemberRemovedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnMemberRemoved.Url=$servuri/twilio/receive' -d 'Webhooks.OnMemberRemoved.Method=POST' -d 'Webhooks.OnMemberRemoved.Format=XML' -u '$accsid:$authtok'" -eval $onMemberRemovedCurl -onMessageRemovedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnMessageRemoved.Url=$servuri/twilio/receive' -d 'Webhooks.OnMessageRemoved.Method=POST' -d 'Webhooks.OnMessageRemoved.Format=XML' -u '$accsid:$authtok'" -eval $onMessageRemovedCurl - -onMessageUpdatedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnMessageUpdated.Url=$servuri/twilio/receive' -d 'Webhooks.OnMessageUpdated.Method=POST' -d 'Webhooks.OnMessageUpdated.Format=XML' -u '$accsid:$authtok'" -eval $onMessageUpdatedCurl - -onChannelUpdatedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnChannelUpdated.Url=$servuri/twilio/receive' -d 'Webhooks.OnChannelUpdated.Method=POST' -d 'Webhooks.OnChannelUpdated.Format=XML' -u '$accsid:$authtok'" -eval $onChannelUpdatedCurl - -onMemberAddedCurl="curl -X POST https://ip-messaging.twilio.com/v1/Services/$servsid -d 'Webhooks.OnMemberAdded.Url=$servuri/twilio/receive' -d 'Webhooks.OnMemberAdded.Method=POST' -d 'Webhooks.OnMemberAdded.Format=XML' -u '$accsid:$authtok'" -eval $onMemberAddedCurl -``` - -When you are ready to go live, consider [LetsEncrypt.org](http://letsencrypt.org), a _free_ SSL Certificate Signing Authority which can be used to secure your website very quickly. It is fabulous and we love it. +When you are ready to go live, consider [LetsEncrypt.org](http://letsencrypt.org), a _free_ SSL Certificate Signing Authority which can be used to secure your website very quickly. -## Twilio IPM Specific Events +## Bot Framework Specific Events -Once connected to your Twilio IPM service, bots receive a constant stream of events. +Once connected to the Bot Framework, bots receive a constant stream of events. -Normal messages will be sent to your bot using the `message_received` event. In addition, Botkit will trigger these Botkit-specific events: +Normal messages will be sent to your bot using the `message_received` event. In addition, several other events may fire, depending on the channel your bot is configured to support. | Event | Description |--- |--- -| bot_channel_join| The bot has joined a channel -| bot_channel_leave | The bot has left a channel -| user_channel_join | A user (not the bot) has joined a channel -| user_channel_leave | A user (not the bot) has left a channel - -Botkit will handle and distribute [all of the Twilio IPM API webhooks events](https://www.twilio.com/docs/api/ip-messaging/webhooks). Your Bot can act on any of these events, and will receive the complete payload from Twilio. Below, is a list of the IPM API callback events that can be subscribed to in your Bot: +| message_received | A message was received by the bot. Passed an [IMessage](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.imessage.html) object. +| conversationUpdate | Your bot was added to a conversation or other conversation metadata changed. Passed an [IConversationUpdate](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.iconversationupdate.html) object. +| contactRelationUpdate | The bot was added to or removed from a user's contact list. Passed an [IContactRelationUpdate](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.icontactrelationupdate.html) object. +| typing | The user or bot on the other end of the conversation is typing. Passed an [IEvent](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.ievent.html) object. -| Event | Description -|--- |--- -| onMessageSent | Message sent -| onMessageRemoved | Message removed/deleted -| onMessageUpdated | Message edited -| onChannelAdded | Channel created -| onChannelUpdated | Channel FriendlyName or Attributes updated -| onChannelDestroyed | Channel Deleted/Destroyed -| onMemberAdded | Channel Member Joined or Added -| onMemberRemoved | Channel Member Removed or Left +In addition to the event specific fields, all incoming events will contain both `user` and `channel` fields which can be used for things like storage keys. Every event also has an [address](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.ievent.html#address) field. The `user` field is just a copy of the events `address.user.id` field and the `channel` field is a copy of the events `address.conversationId.id` field. +Other notable fields for incoming messages are the [text](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.imessage.html#text) field which contains the text of the incoming message, the [attachments](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.imessage.html#attachments) field which would contain an array of any images sent to the bot by the user, the [source](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.imessage.html#source) field which identifies the type of chat platform (facebook, skype, sms, etc.) that the bot is communicating over, and the [sourceEvent](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.imessage.html#sourceevent) field containing the original event/message received from the chat platform. -## Working with Twilio IP Messaging +## Working with the Bot Framework -Botkit receives messages from Twilio IPM using Webhooks, and sends messages using Twilio's REST APIs. This means that your Bot application must present a web server that is publicly addressable. Everything you need to get started is already included in Botkit. +Botkit receives messages from the Bot Framework using webhooks, and sends messages to the Bot Framework using APIs. This means that your bot application must present a web server that is publicly addressable. Everything you need to get started is already included in Botkit. -To connect your bot to Twilio, [follow the instructions here](https://www.twilio.com/user/account/ip-messaging/getting-started). You will need to collect 5 separate pieces of your API credentials. A step by step guide [can be found here](#getting-started). Since you must *already be running* your Botkit app to fully configure your Twilio app, there is a bit of back-and-forth. It's ok! You can do it. +To connect your bot to the Bot Framework follow the step by step guide outlined in [Getting Started](#getting-started). -Here is the complete code for a basic Twilio bot: +Here is the complete code for a basic Bot Framework bot: ```javascript var Botkit = require('botkit'); -var controller = Botkit.twilioipmbot({ - debug: false -}) +var controller = Botkit.botframeworkbot({ + appId: process.env.app_id, + appPassword: process.env.app_password, +}); var bot = controller.spawn({ - TWILIO_IPM_SERVICE_SID: process.env.TWILIO_IPM_SERVICE_SID, - TWILIO_ACCOUNT_SID: process.env.TWILIO_ACCOUNT_SID, - TWILIO_API_KEY: process.env.TWILIO_API_KEY, - TWILIO_API_SECRET: process.env.TWILIO_API_SECRET, - identity: 'Botkit', - autojoin: true }); // if you are already using Express, you can use your own server instance... @@ -183,6 +91,13 @@ controller.setupWebserver(process.env.port,function(err,webserver) { }); }); +// this is triggered when a user adds a bot to their contact list +controller.on('contactRelationUpdate', function(bot, message) { + + bot.reply(message, 'Welcome to my app!'); + +}); + // user said hello controller.hears(['hello'], 'message_received', function(bot, message) { @@ -202,7 +117,12 @@ controller.hears(['cookies'], 'message_received', function(bot, message) { }); }); ``` +#### Botkit.botframeworkbot() +| Argument | Description +|--- |--- +| settings | Options used to configure the bot. Supports fields from [IChatConnectorSettings](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.ichatconnectorsettings.html). +Creates a new instance of the bots controller. The controller will create a new [ChatConnector](https://docs.botframework.com/en-us/node/builder/chat-reference/classes/_botbuilder_d_.chatconnector.html) so any options needed to configure the chat connector should be passed in via the _settings_ argument. #### controller.setupWebserver() | Argument | Description @@ -214,97 +134,14 @@ Setup an [Express webserver](http://expressjs.com/en/index.html) for use with `createWebhookEndpoints()` If you need more than a simple webserver to receive webhooks, -you should by all means create your own Express webserver! +you should by all means create your own Express webserver! Here is a [boilerplate demo](https://github.com/mvaragnat/botkit-messenger-express-demo). The callback function receives the Express object as a parameter, which may be used to add further web server routes. #### controller.createWebhookEndpoints() -This function configures the route `https://_your_server_/twilio/receive` -to receive webhooks from twilio. - -This url should be used when configuring Twilio. - -## System Bots vs User Bots - -Bots inside a Twilio IPM environment can run in one of two ways: as the "system" user, -ever present and automatically available in all channels, OR, as a specific "bot" user -who must be added to channels in order to interact. - -By default, bots are "system" users, and can be configured as below: - -``` -var bot = controller.spawn({ - TWILIO_IPM_SERVICE_SID: process.env.TWILIO_IPM_SERVICE_SID, - TWILIO_ACCOUNT_SID: process.env.TWILIO_ACCOUNT_SID, - TWILIO_API_KEY: process.env.TWILIO_API_KEY, - TWILIO_API_SECRET: process.env.TWILIO_API_SECRET, -}); -``` - -To connect as a "bot" user, pass in an `identity` field: - -``` -var bot = controller.spawn({ - TWILIO_IPM_SERVICE_SID: process.env.TWILIO_IPM_SERVICE_SID, - TWILIO_ACCOUNT_SID: process.env.TWILIO_ACCOUNT_SID, - TWILIO_API_KEY: process.env.TWILIO_API_KEY, - TWILIO_API_SECRET: process.env.TWILIO_API_SECRET, - identity: 'My Bot Name', -}); -``` - -To have your bot automatically join every channel as they are created and removed, -pass in `autojoin`: +This function configures the route `https://_your_server_/botframework/receive` +to receive webhooks from the Bot Framework. -``` -var bot = controller.spawn({ - TWILIO_IPM_SERVICE_SID: process.env.TWILIO_IPM_SERVICE_SID, - TWILIO_ACCOUNT_SID: process.env.TWILIO_ACCOUNT_SID, - TWILIO_API_KEY: process.env.TWILIO_API_KEY, - TWILIO_API_SECRET: process.env.TWILIO_API_SECRET, - identity: 'Botkit', - autojoin: true -}); -``` - -## Using the Twilio API - -You can use the Twilio API directly in your Bot via Botkit's bot.api object. Botkit's bot.api provides a thin wrapper on the [Twilio official module](http://twilio.github.io/twilio-node/). - -For example, to [retrieve a member from a channel](https://www.twilio.com/docs/api/ip-messaging/rest/members#action-get) using the un-wrapped Twilio API client, you would use the following code: - -```javascript -service.channels('CHANNEL_SID').members('MEMBER_SID').get().then(function(response) { - console.log(response); -}).fail(function(error) { - console.log(error); -}); -``` - -In Botkit, this can be accomplished by simply replacing the reference to a `service` object, with the `bot.api` object, as shown here: - -```javascript -bot.api.channels('CHANNEL_SID').members('MEMBER_SID').get().then(function(response) { - console.log(response); -}).fail(function(error) { - console.log(error); -}); -``` -This gives you full access to all of the Twilio API methods so that you can use them in your Bot. - -Here is an example showing how to join a channel using Botkit's bot.api object, which creates a member to the channel, by wrapping the IPM API. - -```javascript -controller.on('onChannelAdded', function(bot, message){ - // whenever a channel gets added, join it! - bot.api.channels(message.channel).members.create({ - identity: bot.identity - }).then(function(response) { - - }).fail(function(error) { - console.log(error); - }); -}); -``` +This url should be used when configuring Facebook. From cb934823178c859026dec9fc586d1c203170b02d Mon Sep 17 00:00:00 2001 From: Steven Ickman Date: Mon, 26 Sep 2016 08:45:54 -0700 Subject: [PATCH 4/5] Finished initial botframework readme. --- readme-botframework.md | 78 +++++++++++++++++++++++++++++++++++++----- readme.md | 1 + 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/readme-botframework.md b/readme-botframework.md index 5be664090..5b1792642 100644 --- a/readme-botframework.md +++ b/readme-botframework.md @@ -15,7 +15,9 @@ Table of Contents * [Getting Started](#getting-started) * [Bot Framework Specific Events](#bot-framework-specific-events) -* [Working with the Bot Framework IPM](#working-with-the-bot-framework) +* [Working with the Bot Framework](#working-with-the-bot-framework) +* [Sending Cards and Attachments](#sending-cards-and-attachments) +* [Typing Indicator](#typing-indicator) ## Getting Started @@ -91,13 +93,6 @@ controller.setupWebserver(process.env.port,function(err,webserver) { }); }); -// this is triggered when a user adds a bot to their contact list -controller.on('contactRelationUpdate', function(bot, message) { - - bot.reply(message, 'Welcome to my app!'); - -}); - // user said hello controller.hears(['hello'], 'message_received', function(bot, message) { @@ -122,7 +117,9 @@ controller.hears(['cookies'], 'message_received', function(bot, message) { |--- |--- | settings | Options used to configure the bot. Supports fields from [IChatConnectorSettings](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.ichatconnectorsettings.html). -Creates a new instance of the bots controller. The controller will create a new [ChatConnector](https://docs.botframework.com/en-us/node/builder/chat-reference/classes/_botbuilder_d_.chatconnector.html) so any options needed to configure the chat connector should be passed in via the _settings_ argument. +Creates a new instance of the bots controller. The controller will create a new [ChatConnector](https://docs.botframework.com/en-us/node/builder/chat-reference/classes/_botbuilder_d_.chatconnector.html) so any options needed to configure the chat connector should be passed in via the `settings` argument. + +Generally speaking your bot needs to be configured with both an [appId](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.ichatconnectorsettings.html#appid) and [appPassword](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.ichatconnectorsettings.html#apppassword). You can leave these blank but then your bot can only be called by the [Bot Framework Emulator](https://docs.botframework.com/en-us/tools/bot-framework-emulator/#navtitle). #### controller.setupWebserver() | Argument | Description @@ -145,3 +142,66 @@ This function configures the route `https://_your_server_/botframework/receive` to receive webhooks from the Bot Framework. This url should be used when configuring Facebook. + +## Sending Cards and Attachments + +One of the more complicated aspects of building a bot that supports multiple chat platforms is dealing with all the various schemes these platforms support. To help ease this development burden, the Bot Framework supports a cross platform card & attachment schema which lets you use a single JSON schema to express cards and attachments for any platform. The Bot Frameworks channel adapters will do their best to render a card on a given platform which sometimes result in a card being broken up into multiple messages. + +#### Sending Images & Files + +The frameworks [attachment schema](https://docs.botframework.com/en-us/node/builder/chat-reference/interfaces/_botbuilder_d_.iattachment.html) lets you send a single image/file using `contentType` and `contentUrl` fields: + +```javascript + bot.reply(message, { + attachments: [ + { + contentType: 'image/png', + contentUrl: 'https://upload.wikimedia.org/wikipedia/en/a/a6/Bender_Rodriguez.png', + name: 'Bender_Rodriguez.png' + } + ] + }); +``` + +#### Sending Cards + +Rich cards can be expressed as attachments by changing the `contentType` and using the `content` field to pass a JSON object defining the card: + +```javascript + bot.reply(message, { + attachments: [ + { + contentType: 'application/vnd.microsoft.card.hero', + content: { + title: "I'm a hero card", + subtitle: "Pig Latin Wikipedia Page", + images: [ + { url: "https://" }, + { url: "https://" } + ], + buttons: [ + { + type: "openUrl", + title: "WikiPedia Page", + value: "https://en.wikipedia.org/wiki/Pig_Latin" + } + ] + } + } + ] + }); +``` + +The full list of supported card types and relevant schema can be found [here](https://docs.botframework.com/en-us/csharp/builder/sdkreference/attachments.html) + +#### Using the Platforms Native Schema + +There may be times where the Bot Frameworks cross platform attachment schema doesn’t cover your needs. For instance, you may be trying to send a card type not directly supported by the framework. In those cases you can pass a message using the platforms native schema to the `sourceEvent` field on the message. Examples of this can be found [here](https://docs.botframework.com/en-us/csharp/builder/sdkreference/channels.html) (note: you should use `sourceEvent` instead of `channelData` and you don’t need to worry about the from & to fields, these will be populated for you when you call `bot.reply()`.) + +## Typing Indicator + +You can easily turn on the typing indicator on platforms that support that behaviour by sending an empty message of type "typing": + +```javascript + bot.reply(message, { type: "typing" }); +``` diff --git a/readme.md b/readme.md index a180b2398..6a644f6b3 100755 --- a/readme.md +++ b/readme.md @@ -13,6 +13,7 @@ Botkit features a comprehensive set of tools to deal with popular messaging plat * [Slack](readme-slack.md) * [Facebook Messenger](readme-facebook.md) * [Twilio IP Messaging](readme-twilioipm.md) +* [Microsoft Bot Framework](readme-botframework.md) * Yours? [info@howdy.ai](mailto:info@howdy.ai) ## Installation From 643f35069c96a4fbb3ff7a190167af1fa57d298e Mon Sep 17 00:00:00 2001 From: Steven Ickman Date: Mon, 26 Sep 2016 08:54:50 -0700 Subject: [PATCH 5/5] updated comments section of sample --- botframework_bot.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/botframework_bot.js b/botframework_bot.js index dd926a9e6..4a14691c8 100644 --- a/botframework_bot.js +++ b/botframework_bot.js @@ -19,19 +19,17 @@ This bot demonstrates many of the core features of Botkit: # RUN THE BOT: - Follow the instructions here to set up your Facebook app and page: - - -> https://developers.facebook.com/docs/messenger-platform/implementation + Follow the instructions in the "Getting Started" section of the readme-botframework.md file to register your bot. Run your bot from the command line: - page_token= verify_token= node facebook_bot.js [--lt [--ltsubdomain LOCALTUNNEL_SUBDOMAIN]] + app_id= app_password= node botframework_bot.js [--lt [--ltsubdomain LOCALTUNNEL_SUBDOMAIN]] Use the --lt option to make your bot available on the web through localtunnel.me. # USE THE BOT: - Find your bot inside Facebook to send it a direct message. + Find your bot inside Skype to send it a direct message. Say: "Hello"