From f035d72ff76706ba08a3d9e21d5edd4101aa1bdc Mon Sep 17 00:00:00 2001 From: Tony Thomson Date: Wed, 16 Dec 2015 17:48:37 -0800 Subject: [PATCH 01/35] Fix param for bot.say() example code in readme.md --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 06447001a..11bcc3b66 100755 --- a/readme.md +++ b/readme.md @@ -222,7 +222,7 @@ for more information about listening for and responding to messages. It is also possible to bind event handlers directly to any of the enormous number of native Slack events, as well as a handful of custom events emitted by Botkit. -You can receive and handle any of the [native events thrown by slack](https://api.slack.com/events). +You can receive and handle any of the [native events thrown by slack](https://api.slack.com/events). ```javascript controller.on('channel_joined',function(bot,message) { @@ -522,7 +522,7 @@ controller.hears(['question me'],['direct_message','direct_mention','mention','a pattern: 'done', callback: function(response,convo) { convo.say('OK you are done!'); - convo.next(); + convo.next(); } }, { @@ -641,7 +641,7 @@ respond to incoming messages, you may want to use [Slack's incoming webhooks fea bot.say( { text: 'my message text', - channel: '#channel' + channel: 'G0GRHT83Z' } ); ``` From 3130e9f0c9be9905ef02066ddb5a406a97203450 Mon Sep 17 00:00:00 2001 From: brianleroux Date: Wed, 16 Dec 2015 22:48:30 -0800 Subject: [PATCH 02/35] adding some test infra but need some help --- .gitignore | 1 + package.json | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) mode change 100755 => 100644 package.json diff --git a/.gitignore b/.gitignore index 1819a7085..c3c36444b 100755 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ examples/db_slackbutton_slashcommand/ examples/db_team_bot/ .DS_Store */.DS_Store +.env diff --git a/package.json b/package.json old mode 100755 new mode 100644 index b0f9e1078..a0e3bd114 --- a/package.json +++ b/package.json @@ -11,9 +11,13 @@ "request": "^2.60.0", "ws": "^0.7.2" }, - "devDependencies": {}, + "devDependencies": { + "node-env-file": "^0.1.8", + "tap-spec": "^4.1.1", + "tape": "^4.2.2" + }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "node test/*-test.js | tap-spec" }, "repository": { "type": "git", From b96bedc7193a998509b6976e90c83ae4d95a458b Mon Sep 17 00:00:00 2001 From: brianleroux Date: Wed, 16 Dec 2015 22:48:41 -0800 Subject: [PATCH 03/35] adding tests --- test/Slack_web_api-test.js | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 test/Slack_web_api-test.js diff --git a/test/Slack_web_api-test.js b/test/Slack_web_api-test.js new file mode 100644 index 000000000..b2a7f2c27 --- /dev/null +++ b/test/Slack_web_api-test.js @@ -0,0 +1,39 @@ +var test = require('tape') +var Botkit = require('../') +var env = require('node-env-file') +var path = require('path') + +env(path.join(__dirname, '..', '.env')) +var token = {token:process.env.TOKEN} + +test('sanity', t=> { + t.plan(4) + t.ok(token, '.env sets TOKEN') + console.log(token) + t.ok(Botkit, 'Botkit exists') + t.ok(Botkit.core, 'Botkit.core exists') + t.ok(Botkit.slackbot, 'Botkit.slackbot exists') + console.log(Botkit) +}) + +test('can start and then stop a bot', t=> { + + var controller = Botkit.slackbot({debug:false}) + var bot = controller.spawn(token).startRTM((err, bot, payload)=> { + + if (err) { + t.fail(err, err) + } + else { + t.ok(bot, 'got the bot') + console.log(Object.keys(bot), payload) + } + + // does not exit! + bot.rtm.terminate() + bot.closeRTM() + + t.end() + }) +}) + From da3af814e8b3f06cf7f339540a1b1ab40f5d7b5f Mon Sep 17 00:00:00 2001 From: brianleroux Date: Fri, 18 Dec 2015 11:03:15 -0800 Subject: [PATCH 04/35] added clearinterval so Botkit cleanly exits on closeRTM --- lib/Slackbot_worker.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Slackbot_worker.js b/lib/Slackbot_worker.js index 0b6e73711..d1bc12602 100755 --- a/lib/Slackbot_worker.js +++ b/lib/Slackbot_worker.js @@ -50,7 +50,9 @@ module.exports = function(botkit,config) { } bot.closeRTM = function() { - + // set in ./CoreBot + clearInterval(bot.botkit.tickInterval); + // if (bot.rtm) { bot.rtm.close(); } From 6b8369e12d2d0035a48e74e3942cc10a4e542065 Mon Sep 17 00:00:00 2001 From: Andy Jiang Date: Sat, 19 Dec 2015 23:52:02 -0500 Subject: [PATCH 05/35] added option to pass redirect_uri --- lib/SlackBot.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/SlackBot.js b/lib/SlackBot.js index 998f6e7c5..3cee5e954 100755 --- a/lib/SlackBot.js +++ b/lib/SlackBot.js @@ -25,6 +25,7 @@ function Slackbot(configuration) { } else { slack_botkit.config.clientId = slack_app_config.clientId; slack_botkit.config.clientSecret = slack_app_config.clientSecret; + if (slack_app_config.redirectUri) slack_botkit.config.redirectUri = slack_app_config.redirectUri;; if (typeof(slack_app_config.scopes)=='string') { slack_botkit.config.scopes = slack_app_config.scopes.split(/\,/); } else { @@ -194,8 +195,8 @@ function Slackbot(configuration) { if (team_id) { url = url + "&team=" + team_id; } - if (slack_botkit.config.redirect_uri) { - url = url + "&redirect_uri="+redirect_uri; + if (slack_botkit.config.redirectUri) { + url = url + "&redirect_uri="+slack_botkit.config.redirectUri; } return url; @@ -260,11 +261,15 @@ function Slackbot(configuration) { var code = req.query.code; var state = req.query.state; - oauth_access({ + var opts = { client_id: slack_botkit.config.clientId, client_secret: slack_botkit.config.clientSecret, code: code - },function(err,auth) { + }; + + if (slack_botkit.config.redirectUri) opts.redirect_uri = slack_botkit.config.redirectUri; + + oauth_access(opts,function(err,auth) { if (err) { if (callback) { From a11c4fbe83c31bffc311ef8c9df9ec5ec613be88 Mon Sep 17 00:00:00 2001 From: Andy Jiang Date: Sun, 20 Dec 2015 11:32:34 -0500 Subject: [PATCH 06/35] added mongo storage client --- lib/mongo_storage.js | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 lib/mongo_storage.js diff --git a/lib/mongo_storage.js b/lib/mongo_storage.js new file mode 100644 index 000000000..e10f2a414 --- /dev/null +++ b/lib/mongo_storage.js @@ -0,0 +1,49 @@ +var db = require('monk'); + +module.exports = function(path) { + + if (!path) throw new Error('Need to provide mongo address.'); + + var teams_db = db(path).get('teams'); + var users_db = db(path).get('users'); + var channels_db = db(path).get('channels'); + + + var storage = { + teams: { + get: function(team_id,cb) { + teams_db.find({id: team_id}, cb); + }, + save: function(team,cb) { + teams_db.findAndModify({id: team.id},team,{upsert: true, new: true}, cb); + }, + all: function(cb) { + teams_db.find({}, cb); + } + }, + users: { + get: function(user_id,cb) { + users_db.find({id: user_id}, cb); + }, + save: function(user,cb) { + users_db.findAndModify({id: user.id}, user, {upsert: true, new: true}, cb); + }, + all: function(cb) { + users_db.find({}, cb); + } + }, + channels: { + get: function(channel_id, cb) { + channels_db.find({id: channel_id}, cb); + }, + save: function(channel, cb) { + channels_db.findAndModify({id: channel.id}, channel, {upsert: true, new: true}, cb); + }, + all: function(cb) { + channels_db.find({}, cb); + } + } + }; + + return storage; +}; From b4c794b7160971b9720c380db579ce761d20e165 Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Sun, 20 Dec 2015 09:00:44 -0800 Subject: [PATCH 07/35] Adds file comment and example usage to simple_storage.js --- lib/simple_storage.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/simple_storage.js b/lib/simple_storage.js index 4e126f187..4289d0e96 100755 --- a/lib/simple_storage.js +++ b/lib/simple_storage.js @@ -1,3 +1,27 @@ +/* +Storage module for bots. + +Supports storage of data on a team-by-team, user-by-user, and chnnel-by-channel basis. + +save can be used to store arbitrary object. +These objects must include an id by which they can be looked up. +It is recommended to use the team/user/channel id for this purpose. +Example usage of save: +controller.storage.teams.save({id: message.team, foo:"bar"}, function(err){ + if (err) + console.log(err) +}); + +get looks up an object by id. +Example usage of get: +controller.storage.teams.get(message.team, function(err, team_data){ + if (err) + console.log(err) + else + console.log(team_data) +}); +*/ + var Store = require("jfs"); module.exports = function(config) { From 97cdcfc62b1fa2ee342a241ba1b1796cc1bda74b Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Sun, 20 Dec 2015 09:12:38 -0800 Subject: [PATCH 08/35] Update readme.md --- readme.md | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/readme.md b/readme.md index 11bcc3b66..28333f1db 100755 --- a/readme.md +++ b/readme.md @@ -881,33 +881,46 @@ bot.api.channels.list({},function(err,response) { Botkit has a built in storage system used to keep data on behalf of users and teams between sessions. Botkit uses this system automatically when storing information for Slack Button applications (see below). -By default, Botkit will use [json-file-store](https://github.com/flosse/json-file-store) to keep data in JSON files in the filesystem of the computer where the bot is executed. (Note this will not work on Heroku or other hosting systems that do not let node applications write to the file system.) +By default, Botkit will use [json-file-store](https://github.com/flosse/json-file-store) to keep data in JSON files in the filesystem of the computer where the bot is executed. (Note this will not work on Heroku or other hosting systems that do not let node applications write to the file system.) Initialize this system when you create the bot: -Support for freeform storage for teams, users and channels. -Basically this is a key value store. You can pass in + +Support for freeform storage on a team-by-team, user-by-user, and channel-by-channel basis. +Basically this is a key value store. + +These objects must include an id by which they can be looked up. +It is recommended to use the team/user/channel id for this purpose. +You can pass in whatever data you like to any of these, as long as it has an ID field, which should be a Slack unique id. +Storage module for bots. +Supports storage of data on a team-by-team, user-by-user, and chnnel-by-channel basis. +save can be used to store arbitrary object. -```javascript -var controller = Botkit.slackbot({ - json_file_store: 'path_to_json_database' +Example usage of save: +controller.storage.teams.save({id: message.team, foo:"bar"}, function(err){ + if (err) + console.log(err) +}); +get looks up an object by id. +Example usage of get: +controller.storage.teams.get(message.team, function(err, team_data){ + if (err) + console.log(err) + else + console.log(team_data) }); +``` +```javascript controller.storage.teams.save(team_data,function(err) { ... }); -controller.storage.teams.get(id,function(err,team_data) { - ... -}); +controller.storage.teams.get(id,function(err,team_data) {...}); controller.storage.users.save(user_data,function(err) { ... }); -controller.storage.users.get(id,function(err,user_data) { - ... -}); +controller.storage.users.get(id,function(err,user_data) {...}); controller.storage.channels.save(channel_data,function(err) { ... }); -controller.storage.channels.get(id,function(err,channel_data) { - ... -}); +controller.storage.channels.get(id,function(err,channel_data) {...}); ``` ### Write your own storage provider From ae395b54f2ccf5ab46be66e200b38c6eb9999330 Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Sun, 20 Dec 2015 09:21:20 -0800 Subject: [PATCH 09/35] Update readme.md --- readme.md | 45 ++++++++++++--------------------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/readme.md b/readme.md index 28333f1db..6120895c4 100755 --- a/readme.md +++ b/readme.md @@ -878,52 +878,31 @@ bot.api.channels.list({},function(err,response) { ## Storing Information -Botkit has a built in storage system used to keep data -on behalf of users and teams between sessions. Botkit uses this system automatically when storing information for Slack Button applications (see below). +Botkit has a built in storage system used to keep data on behalf of users and teams between sessions. Botkit uses this system automatically when storing information for Slack Button applications (see below). By default, Botkit will use [json-file-store](https://github.com/flosse/json-file-store) to keep data in JSON files in the filesystem of the computer where the bot is executed. (Note this will not work on Heroku or other hosting systems that do not let node applications write to the file system.) Initialize this system when you create the bot: - - -Support for freeform storage on a team-by-team, user-by-user, and channel-by-channel basis. -Basically this is a key value store. - -These objects must include an id by which they can be looked up. -It is recommended to use the team/user/channel id for this purpose. -You can pass in -whatever data you like to any of these, as long as it has an -ID field, which should be a Slack unique id. - -Storage module for bots. -Supports storage of data on a team-by-team, user-by-user, and chnnel-by-channel basis. -save can be used to store arbitrary object. - -Example usage of save: -controller.storage.teams.save({id: message.team, foo:"bar"}, function(err){ - if (err) - console.log(err) -}); -get looks up an object by id. -Example usage of get: -controller.storage.teams.get(message.team, function(err, team_data){ - if (err) - console.log(err) - else - console.log(team_data) +```javascript +var controller = Botkit.slackbot({ + json_file_store: 'path_to_json_database' }); ``` +This system supports freeform storage on a team-by-team, user-by-user, and channel-by-channel basis. All access to this system is through ```controller.storage.[teams/users/channels].save``` and ```controller.storage.[teams/users/channels].get```. Basically it is a key value store. Example usage: ```javascript -controller.storage.teams.save(team_data,function(err) { ... }); +controller.storage.teams.save({id: message.team, foo:"bar"},function(err) { ... }); controller.storage.teams.get(id,function(err,team_data) {...}); -controller.storage.users.save(user_data,function(err) { ... }); +controller.storage.users.save({id: message.user, foo:"bar"},function(err) { ... }); controller.storage.users.get(id,function(err,user_data) {...}); -controller.storage.channels.save(channel_data,function(err) { ... }); +controller.storage.channels.save({id: message.channel, foo:"bar"},function(err) { ... }); controller.storage.channels.get(id,function(err,channel_data) {...}); ``` -### Write your own storage provider +Note that save must be passed an object with an id. It is recommended to use the team/user/channel id for this purpose. +You can pass in whatever data you like to any of these, as long as it has an ID field, which should be a Slack unique id. + +### Writing your own storage provider If you want to use a database or do something else with your data, you can write your own storage module and pass it in. From 443acf736e2404a8c45d4aad34d3874ca1586401 Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Sun, 20 Dec 2015 09:22:54 -0800 Subject: [PATCH 10/35] Update readme.md --- readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/readme.md b/readme.md index 6120895c4..426d1c153 100755 --- a/readme.md +++ b/readme.md @@ -900,7 +900,6 @@ controller.storage.channels.get(id,function(err,channel_data) {...}); ``` Note that save must be passed an object with an id. It is recommended to use the team/user/channel id for this purpose. -You can pass in whatever data you like to any of these, as long as it has an ID field, which should be a Slack unique id. ### Writing your own storage provider From f5bff6d3a087c2c36c3a1d957df87d615fd97401 Mon Sep 17 00:00:00 2001 From: Guillaume Potier Date: Tue, 22 Dec 2015 00:33:02 +0100 Subject: [PATCH 11/35] inject RegExp match into message --- lib/CoreBot.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/CoreBot.js b/lib/CoreBot.js index 42a34a5c4..0e2755dd8 100755 --- a/lib/CoreBot.js +++ b/lib/CoreBot.js @@ -640,14 +640,16 @@ function Botkit(configuration) { events = events.split(/\,/g); } + var match; for (var k = 0; k < keywords.length; k++) { var keyword = keywords[k]; for (var e = 0; e < events.length; e++) { (function(keyword) { botkit.on(events[e],function(bot,message) { if (message.text) { - if (message.text.match(new RegExp(keyword,'i'))) { + if (match = message.text.match(new RegExp(keyword,'i'))) { botkit.debug("I HEARD ",keyword); + message.match = match; cb.apply(this,[bot,message]); return false; } From 0fbc47a0998e0d5ddd1285c12fdc375b5b874f93 Mon Sep 17 00:00:00 2001 From: Guillaume Potier Date: Tue, 22 Dec 2015 00:45:21 +0100 Subject: [PATCH 12/35] updated README --- readme.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/readme.md b/readme.md index 426d1c153..aaa79c8f1 100755 --- a/readme.md +++ b/readme.md @@ -336,6 +336,16 @@ controller.hears(['keyword','^pattern$'],['direct_message','direct_mention','men }); ``` +```javascript +controller.hears('open the (.*) doors',['direct_message','direct_mention','mention','ambient'],function(bot,message) { + var doorType = message.match[1]; + if (doorType === 'back') { + return bot.reply(message, 'I cannot do that'); + } + return bot.reply(message, 'Okay'); +}); +``` + ## Sending Messages Bots have to send messages to deliver information and present an interface for their From c0d20abecbb0d142e6e58c7b1a30668480eba6c6 Mon Sep 17 00:00:00 2001 From: Alex Flores Date: Tue, 22 Dec 2015 12:32:32 -0500 Subject: [PATCH 13/35] fixes slash_command and outgoing webhook functions - fixes 'botkit undefined' error when using webhooks and slash commands --- lib/SlackBot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/SlackBot.js b/lib/SlackBot.js index 3cee5e954..d44daaab9 100755 --- a/lib/SlackBot.js +++ b/lib/SlackBot.js @@ -90,7 +90,7 @@ function Slackbot(configuration) { res.status(200); - var bot = botkit.spawn(team); + var bot = slack_botkit.spawn(team); bot.team_info = team; bot.res = res; @@ -125,7 +125,7 @@ function Slackbot(configuration) { res.status(200); - var bot = botkit.spawn(team); + var bot = slack_botkit.spawn(team); bot.res = res; bot.team_info = team; From 4bb4407b76b2c2d7943f08414d8411a6bdfe4848 Mon Sep 17 00:00:00 2001 From: Will Raxworthy Date: Thu, 24 Dec 2015 10:50:18 +0000 Subject: [PATCH 14/35] reference channel variable when checking id --- lib/CoreBot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CoreBot.js b/lib/CoreBot.js index 0e2755dd8..b7b5cc550 100755 --- a/lib/CoreBot.js +++ b/lib/CoreBot.js @@ -601,7 +601,7 @@ function Botkit(configuration) { }, save: function(channel,cb) { botkit.log('Warning: using temporary storage. Data will be lost when process restarts.') - if (user.id) { + if (channel.id) { botkit.memory_store['channels'][channel.id] = channel; cb(null,channel.id); } else { From dbe20bea0e0c51427f98eed34ad2066d4a2dafdc Mon Sep 17 00:00:00 2001 From: Andy Jiang Date: Thu, 24 Dec 2015 10:28:11 -0500 Subject: [PATCH 15/35] swapped config for path --- lib/mongo_storage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/mongo_storage.js b/lib/mongo_storage.js index e10f2a414..78b436a9b 100644 --- a/lib/mongo_storage.js +++ b/lib/mongo_storage.js @@ -1,8 +1,8 @@ var db = require('monk'); -module.exports = function(path) { +module.exports = function(config) { - if (!path) throw new Error('Need to provide mongo address.'); + if (!config) throw new Error('Need to provide mongo address.'); var teams_db = db(path).get('teams'); var users_db = db(path).get('users'); From a5046a8cef13508d2f8f213996348e3fc3e5138f Mon Sep 17 00:00:00 2001 From: okvic77 Date: Sat, 26 Dec 2015 23:59:53 -0600 Subject: [PATCH 16/35] Package dependencies updated. --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index a0e3bd114..5ccc40dec 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,12 @@ "description": "Building blocks for Building Bots", "main": "lib/Botkit.js", "dependencies": { - "body-parser": "^1.13.3", + "body-parser": "^1.14.2", "express": "^4.13.3", "jfs": "^0.2.6", - "mustache": "^2.1.3", - "request": "^2.60.0", - "ws": "^0.7.2" + "mustache": "^2.2.1", + "request": "^2.67.0", + "ws": "^0.8.1" }, "devDependencies": { "node-env-file": "^0.1.8", From 0542bb2f801a3e0d8e11801b5b7d31f74e84297d Mon Sep 17 00:00:00 2001 From: Andy Jiang Date: Sun, 27 Dec 2015 14:43:55 -0500 Subject: [PATCH 17/35] changed to mongo_uri --- lib/mongo_storage.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/mongo_storage.js b/lib/mongo_storage.js index 78b436a9b..169f1a9e5 100644 --- a/lib/mongo_storage.js +++ b/lib/mongo_storage.js @@ -2,12 +2,11 @@ var db = require('monk'); module.exports = function(config) { - if (!config) throw new Error('Need to provide mongo address.'); - - var teams_db = db(path).get('teams'); - var users_db = db(path).get('users'); - var channels_db = db(path).get('channels'); + if (!config && !config.mongo_uri) throw new Error('Need to provide mongo address.'); + var teams_db = db(config.mongo_uri).get('teams'); + var users_db = db(config.mongo_uri).get('users'); + var channels_db = db(config.mongo_uri).get('channels'); var storage = { teams: { From 4d7323582508094a13073c83287d9072a8fb4378 Mon Sep 17 00:00:00 2001 From: Cyrus Roshan Date: Sun, 27 Dec 2015 15:22:07 -0600 Subject: [PATCH 18/35] typo: cli command had incorrect filename --- examples/demo_bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/demo_bot.js b/examples/demo_bot.js index 5c967cbfd..6b0ca5d5d 100755 --- a/examples/demo_bot.js +++ b/examples/demo_bot.js @@ -23,7 +23,7 @@ This bot demonstrates many of the core features of Botkit: Run your bot from the command line: - token= node team_bot.js + token= node demo_bot.js # USE THE BOT: From 6bffb94e4e0d2c66be960b596f6240cf2ab2373e Mon Sep 17 00:00:00 2001 From: Cyrus Roshan Date: Sun, 27 Dec 2015 15:22:37 -0600 Subject: [PATCH 19/35] feature: made botkit.log optional --- lib/CoreBot.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/CoreBot.js b/lib/CoreBot.js index b7b5cc550..e8ac31590 100755 --- a/lib/CoreBot.js +++ b/lib/CoreBot.js @@ -625,11 +625,13 @@ function Botkit(configuration) { } botkit.log = function() { + if (configuration.log) { var args=[]; for (var k = 0; k < arguments.length; k++) { args.push(arguments[k]); } console.log.apply(null,args); + } } botkit.hears = function(keywords,events,cb) { From bc700ebbe893a4e12ef7d0953a7a3d7031964582 Mon Sep 17 00:00:00 2001 From: Cyrus Roshan Date: Sun, 27 Dec 2015 15:23:01 -0600 Subject: [PATCH 20/35] info: informed about optional logging, removed trailing whitespace --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index aaa79c8f1..ebd493e9f 100755 --- a/readme.md +++ b/readme.md @@ -103,6 +103,7 @@ var Botkit = require('botkit'); var controller = Botkit.slackbot({ debug: false + //include "log: false" to disable logging }); // connect the bot to a stream of messages From de93331bbc7fc81bc667f24b772299a903a9ed22 Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Sun, 27 Dec 2015 22:02:08 +0000 Subject: [PATCH 21/35] Adds comments to mongo_storage.js --- lib/mongo_storage.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/mongo_storage.js b/lib/mongo_storage.js index 169f1a9e5..a825b0bff 100644 --- a/lib/mongo_storage.js +++ b/lib/mongo_storage.js @@ -1,8 +1,11 @@ -var db = require('monk'); +var db = require('monk'); //https://www.npmjs.com/package/monk module.exports = function(config) { if (!config && !config.mongo_uri) throw new Error('Need to provide mongo address.'); + /* Your mongo_uri will look something like + 'mongodb://test:test@ds037145.mongolab.com:37145/slack-bot-test' + or 'localhost/mydb,192.168.1.1' */ var teams_db = db(config.mongo_uri).get('teams'); var users_db = db(config.mongo_uri).get('users'); From bbe6c99785fa76d4158705af5f9037036dd4e069 Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Mon, 28 Dec 2015 19:48:30 +0000 Subject: [PATCH 22/35] Makes interface to simple_storage and mongo_storage consistent --- lib/CoreBot.js | 2 +- lib/simple_storage.js | 78 -------------------------- lib/{ => storage}/mongo_storage.js | 32 +++++++---- lib/storage/simple_storage.js | 89 ++++++++++++++++++++++++++++++ lib/storage/storage_test.js | 38 +++++++++++++ 5 files changed, 149 insertions(+), 90 deletions(-) delete mode 100755 lib/simple_storage.js rename lib/{ => storage}/mongo_storage.js (51%) create mode 100755 lib/storage/simple_storage.js create mode 100644 lib/storage/storage_test.js diff --git a/lib/CoreBot.js b/lib/CoreBot.js index e8ac31590..36a9318f7 100755 --- a/lib/CoreBot.js +++ b/lib/CoreBot.js @@ -2,7 +2,7 @@ /* It expects to receive messages via the botkit.receiveMessage function */ /* These messages are expected to match Slack's message format. */ var mustache = require('mustache'); -var simple_storage = require(__dirname+'/simple_storage.js'); +var simple_storage = require(__dirname+'/storage/simple_storage.js'); function Botkit(configuration) { diff --git a/lib/simple_storage.js b/lib/simple_storage.js deleted file mode 100755 index 4289d0e96..000000000 --- a/lib/simple_storage.js +++ /dev/null @@ -1,78 +0,0 @@ -/* -Storage module for bots. - -Supports storage of data on a team-by-team, user-by-user, and chnnel-by-channel basis. - -save can be used to store arbitrary object. -These objects must include an id by which they can be looked up. -It is recommended to use the team/user/channel id for this purpose. -Example usage of save: -controller.storage.teams.save({id: message.team, foo:"bar"}, function(err){ - if (err) - console.log(err) -}); - -get looks up an object by id. -Example usage of get: -controller.storage.teams.get(message.team, function(err, team_data){ - if (err) - console.log(err) - else - console.log(team_data) -}); -*/ - -var Store = require("jfs"); - -module.exports = function(config) { - - if (!config) { - config = { - path: "./", - } - } - - var teams_db = new Store(config.path + "/teams",{saveId: 'id'}); - var users_db = new Store(config.path + "/users",{saveId: 'id'}); - var channels_db = new Store(config.path + "/channels",{saveId: 'id'}); - - - var storage = { - teams: { - get: function(team_id,cb) { - teams_db.get(team_id,cb); - }, - save: function(team,cb) { - teams_db.save(team.id,team,cb); - }, - all: function(cb) { - teams_db.all(cb) - } - }, - users: { - get: function(user_id,cb) { - users_db.get(user_id,cb); - }, - save: function(user,cb) { - users_db.save(user.id,user,cb); - }, - all: function(cb) { - users_db.all(cb) - } - }, - channels: { - get: function(channel_id,cb) { - channels_db.get(channel_id,cb); - }, - save: function(channel,cb) { - channels_db.save(channel.id,channel,cb); - }, - all: function(cb) { - channels_db.all(cb) - } - } - }; - - return storage; - -} diff --git a/lib/mongo_storage.js b/lib/storage/mongo_storage.js similarity index 51% rename from lib/mongo_storage.js rename to lib/storage/mongo_storage.js index a825b0bff..99803b271 100644 --- a/lib/mongo_storage.js +++ b/lib/storage/mongo_storage.js @@ -11,24 +11,34 @@ module.exports = function(config) { var users_db = db(config.mongo_uri).get('users'); var channels_db = db(config.mongo_uri).get('channels'); + var unwrapFromList = function(cb) { + return function(err, data) { + if (err) { + cb(err, data); + } else { + cb(err, data[0]); + } + }; + }; + var storage = { teams: { - get: function(team_id,cb) { - teams_db.find({id: team_id}, cb); + get: function(team_id, cb) { + teams_db.find({id: team_id}, unwrapFromList(cb)); }, - save: function(team,cb) { - teams_db.findAndModify({id: team.id},team,{upsert: true, new: true}, cb); + save: function(team_data, cb) { + teams_db.findAndModify({id: team_data.id}, team_data, {upsert: true, new: true}, cb); }, all: function(cb) { teams_db.find({}, cb); } }, users: { - get: function(user_id,cb) { - users_db.find({id: user_id}, cb); + get: function(user_id, cb) { + users_db.find({id: user_id}, unwrapFromList(cb)); }, - save: function(user,cb) { - users_db.findAndModify({id: user.id}, user, {upsert: true, new: true}, cb); + save: function(user_data, cb) { + users_db.findAndModify({id: user_data.id}, user_data, {upsert: true, new: true}, cb); }, all: function(cb) { users_db.find({}, cb); @@ -36,10 +46,10 @@ module.exports = function(config) { }, channels: { get: function(channel_id, cb) { - channels_db.find({id: channel_id}, cb); + channels_db.find({id: channel_id}, unwrapFromList(cb)); }, - save: function(channel, cb) { - channels_db.findAndModify({id: channel.id}, channel, {upsert: true, new: true}, cb); + save: function(channel_data, cb) { + channels_db.findAndModify({id: channel_data.id}, channel_data, {upsert: true, new: true}, cb); }, all: function(cb) { channels_db.find({}, cb); diff --git a/lib/storage/simple_storage.js b/lib/storage/simple_storage.js new file mode 100755 index 000000000..31e48d614 --- /dev/null +++ b/lib/storage/simple_storage.js @@ -0,0 +1,89 @@ +/* +Storage module for bots. + +Supports storage of data on a team-by-team, user-by-user, and chnnel-by-channel basis. + +save can be used to store arbitrary object. +These objects must include an id by which they can be looked up. +It is recommended to use the team/user/channel id for this purpose. +Example usage of save: +controller.storage.teams.save({id: message.team, foo:"bar"}, function(err){ + if (err) + console.log(err) +}); + +get looks up an object by id. +Example usage of get: +controller.storage.teams.get(message.team, function(err, team_data){ + if (err) + console.log(err) + else + console.log(team_data) +}); +*/ + +var Store = require("jfs"); + +module.exports = function(config) { + + if (!config) { + config = { + path: "./", + } + } + + var teams_db = new Store(config.path + "/teams", {saveId: 'id'}); + var users_db = new Store(config.path + "/users", {saveId: 'id'}); + var channels_db = new Store(config.path + "/channels", {saveId: 'id'}); + + var objectsToList = function(cb) { + return function(err, data) { + if (err) { + cb(err, data) + } else { + cb(err, Object.keys(data).map(function (key) { + return data[key]; + })); + } + + }; + }; + + var storage = { + teams: { + get: function(team_id, cb) { + teams_db.get(team_id, cb); + }, + save: function(team_data, cb) { + teams_db.save(team_data.id, team_data, cb); + }, + all: function(cb) { + teams_db.all(objectsToList(cb)); + } + }, + users: { + get: function(user_id, cb) { + users_db.get(user_id, cb); + }, + save: function(user, cb) { + users_db.save(user.id, user, cb); + }, + all: function(cb) { + users_db.all(objectsToList(cb)); + } + }, + channels: { + get: function(channel_id, cb) { + channels_db.get(channel_id, cb); + }, + save: function(channel, cb) { + channels_db.save(channel.id, channel, cb); + }, + all: function(cb) { + channels_db.all(objectsToList(cb)); + } + } + }; + + return storage; +} diff --git a/lib/storage/storage_test.js b/lib/storage/storage_test.js new file mode 100644 index 000000000..46157b313 --- /dev/null +++ b/lib/storage/storage_test.js @@ -0,0 +1,38 @@ +var test = require('unit.js'); + +testObj1 = {id: "TEST1", foo:"bar1"}; +testObj2 = {id: "TEST2", foo:"bar2"}; + +var check = function(storageMethod) { + storageMethod.save(testObj1, function(err) { + test.assert(!err); + storageMethod.save(testObj2, function(err) { + test.assert(!err); + storageMethod.get(testObj1.id, function(err, data) { + test.assert(!err); + console.log(data); + test.assert(data.foo === testObj1.foo); + }); + + storageMethod.all(function(err, data) { + test.assert(!err); + console.log(data); + test.assert(data[0].foo === testObj1.foo); + }); + }); + }); +}; + +console.log("If no asserts failed then the test has passed!"); + +/* Test simple_storage */ +simple_storage = require('./simple_storage.js')(); +check(simple_storage.users); +check(simple_storage.channels); +check(simple_storage.teams); + +/* Test mongo_storage */ +mongo_storage = require('./mongo_storage.js')({mongo_uri: 'mongodb://test:test@ds037145.mongolab.com:37145/slack-bot-test'}); +check(mongo_storage.users); +check(mongo_storage.channels); +check(mongo_storage.teams); \ No newline at end of file From c52f38d4db787828401c89eff312c78afd36ba46 Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Mon, 28 Dec 2015 19:57:45 +0000 Subject: [PATCH 23/35] Comments storage_test.js --- lib/storage/storage_test.js | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/storage/storage_test.js b/lib/storage/storage_test.js index 46157b313..d1834de0e 100644 --- a/lib/storage/storage_test.js +++ b/lib/storage/storage_test.js @@ -1,3 +1,20 @@ +/* +Tests for storage modules. +This file currently test simple_storage.js and mongo_storage.js. + +If you build a new storage module, +you must add it to this test file before your PR will be considered. +How to add it to this test file: + +Add the following to the bottom of this file: + +// Test + = require('./.js')(); +check(.users); +check(.channels); +check(.teams); +*/ + var test = require('unit.js'); testObj1 = {id: "TEST1", foo:"bar1"}; @@ -25,14 +42,14 @@ var check = function(storageMethod) { console.log("If no asserts failed then the test has passed!"); -/* Test simple_storage */ +// Test simple_storage simple_storage = require('./simple_storage.js')(); check(simple_storage.users); check(simple_storage.channels); check(simple_storage.teams); -/* Test mongo_storage */ +// Test mongo_storage mongo_storage = require('./mongo_storage.js')({mongo_uri: 'mongodb://test:test@ds037145.mongolab.com:37145/slack-bot-test'}); check(mongo_storage.users); check(mongo_storage.channels); -check(mongo_storage.teams); \ No newline at end of file +check(mongo_storage.teams); From 150fade9db30dc497b1b8a080ed1e96730e1ec42 Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Mon, 28 Dec 2015 19:59:31 +0000 Subject: [PATCH 24/35] Renames check->testStorageMethod in storage_test.js --- lib/storage/storage_test.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/storage/storage_test.js b/lib/storage/storage_test.js index d1834de0e..d80855088 100644 --- a/lib/storage/storage_test.js +++ b/lib/storage/storage_test.js @@ -20,7 +20,7 @@ var test = require('unit.js'); testObj1 = {id: "TEST1", foo:"bar1"}; testObj2 = {id: "TEST2", foo:"bar2"}; -var check = function(storageMethod) { +var testStorageMethod = function(storageMethod) { storageMethod.save(testObj1, function(err) { test.assert(!err); storageMethod.save(testObj2, function(err) { @@ -44,12 +44,12 @@ console.log("If no asserts failed then the test has passed!"); // Test simple_storage simple_storage = require('./simple_storage.js')(); -check(simple_storage.users); -check(simple_storage.channels); -check(simple_storage.teams); +testStorageMethod(simple_storage.users); +testStorageMethod(simple_storage.channels); +testStorageMethod(simple_storage.teams); // Test mongo_storage mongo_storage = require('./mongo_storage.js')({mongo_uri: 'mongodb://test:test@ds037145.mongolab.com:37145/slack-bot-test'}); -check(mongo_storage.users); -check(mongo_storage.channels); -check(mongo_storage.teams); +testStorageMethod(mongo_storage.users); +testStorageMethod(mongo_storage.channels); +testStorageMethod(mongo_storage.teams); From 575a7191b74192bf2fe488d0c57b43d0e6814ea6 Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Mon, 28 Dec 2015 20:10:23 +0000 Subject: [PATCH 25/35] Updates storage documentation in README.md --- readme.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/readme.md b/readme.md index ebd493e9f..41137fe50 100755 --- a/readme.md +++ b/readme.md @@ -898,26 +898,31 @@ var controller = Botkit.slackbot({ }); ``` -This system supports freeform storage on a team-by-team, user-by-user, and channel-by-channel basis. All access to this system is through ```controller.storage.[teams/users/channels].save``` and ```controller.storage.[teams/users/channels].get```. Basically it is a key value store. Example usage: +This system supports freeform storage on a team-by-team, user-by-user, and channel-by-channel basis. Basically ```controller.storage``` is a key value store. All access to this system is through the following nine functions. Example usage: ```javascript -controller.storage.teams.save({id: message.team, foo:"bar"},function(err) { ... }); -controller.storage.teams.get(id,function(err,team_data) {...}); +controller.storage.users.save({id: message.user, foo:"bar"}, function(err) { ... }); +controller.storage.users.get(id, function(err, user_data) {...}); +controller.storage.users.all(function(err, all_user_data) {...}); -controller.storage.users.save({id: message.user, foo:"bar"},function(err) { ... }); -controller.storage.users.get(id,function(err,user_data) {...}); +controller.storage.channels.save({id: message.channel, foo:"bar"}, function(err) { ... }); +controller.storage.channels.get(id, function(err, channel_data) {...}); +controller.storage.channels.all(function(err, all_channel_data) {...}); -controller.storage.channels.save({id: message.channel, foo:"bar"},function(err) { ... }); -controller.storage.channels.get(id,function(err,channel_data) {...}); +controller.storage.teams.save({id: message.team, foo:"bar"}, function(err) { ... }); +controller.storage.teams.get(id, function(err, team_data) {...}); +controller.storage.teams.all(function(err, all_team_data) {...}); ``` Note that save must be passed an object with an id. It is recommended to use the team/user/channel id for this purpose. +```[user/channel/team]_data``` will always be an object while ```all_[user/channel/team]_data``` will always be a list of objects. -### Writing your own storage provider +### Writing your own storage module If you want to use a database or do something else with your data, you can write your own storage module and pass it in. -Make sure your module returns an object with all the methods. See [simple_storage.js](https://github.com/howdyai/botkit/blob/master/lib/simple_storage.js) for an example of how it is done! +Make sure your module returns an object with all the methods. See [simple_storage.js](https://github.com/howdyai/botkit/blob/master/lib/storage/simple_storage.js) for an example of how it is done! +Make sure your module passes the test in [storage_test.js](https://github.com/howdyai/botkit/blob/master/lib/storage/storage_test.js). Then, use it when you create your bot: ```javascript From 01a9d3ebac1d10c13cc31628f01e20ccffda82bf Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Mon, 28 Dec 2015 23:47:20 +0000 Subject: [PATCH 26/35] Improves storage_test.js --- lib/storage/storage_test.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/storage/storage_test.js b/lib/storage/storage_test.js index d80855088..1e3646c59 100644 --- a/lib/storage/storage_test.js +++ b/lib/storage/storage_test.js @@ -17,24 +17,27 @@ check(.teams); var test = require('unit.js'); +testObj0 = {id: "TEST0", foo:"bar0"}; testObj1 = {id: "TEST1", foo:"bar1"}; -testObj2 = {id: "TEST2", foo:"bar2"}; var testStorageMethod = function(storageMethod) { - storageMethod.save(testObj1, function(err) { + storageMethod.save(testObj0, function(err) { test.assert(!err); - storageMethod.save(testObj2, function(err) { + storageMethod.save(testObj1, function(err) { test.assert(!err); - storageMethod.get(testObj1.id, function(err, data) { + storageMethod.get(testObj0.id, function(err, data) { test.assert(!err); console.log(data); - test.assert(data.foo === testObj1.foo); + test.assert(data.foo === testObj0.foo); }); storageMethod.all(function(err, data) { test.assert(!err); console.log(data); - test.assert(data[0].foo === testObj1.foo); + test.assert( + data[0].foo === testObj0.foo && data[1].foo === testObj1.foo || + data[0].foo === testObj1.foo && data[1].foo === testObj0.foo + ); }); }); }); From a7a78d89dd89a3b773d872568fae703495b19a97 Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Tue, 29 Dec 2015 00:27:14 +0000 Subject: [PATCH 27/35] Changes default behavior to log=true --- examples/demo_bot.js | 1 + lib/CoreBot.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/demo_bot.js b/examples/demo_bot.js index 6b0ca5d5d..6221d21e9 100755 --- a/examples/demo_bot.js +++ b/examples/demo_bot.js @@ -63,6 +63,7 @@ if (!process.env.token) { var controller = Botkit.slackbot({ debug: false, + log: false }); controller.spawn({ diff --git a/lib/CoreBot.js b/lib/CoreBot.js index 36a9318f7..f6373bd79 100755 --- a/lib/CoreBot.js +++ b/lib/CoreBot.js @@ -625,7 +625,7 @@ function Botkit(configuration) { } botkit.log = function() { - if (configuration.log) { + if (configuration.log || configuration.log === undefined) { //default to true var args=[]; for (var k = 0; k < arguments.length; k++) { args.push(arguments[k]); From 41d11673f6f4388aee20012eb25f7426cb468d13 Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Tue, 29 Dec 2015 01:16:52 +0000 Subject: [PATCH 28/35] Explains why match[1] is the first group --- readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 41137fe50..e1e13d2ee 100755 --- a/readme.md +++ b/readme.md @@ -336,10 +336,11 @@ controller.hears(['keyword','^pattern$'],['direct_message','direct_mention','men }); ``` +For example, ```javascript controller.hears('open the (.*) doors',['direct_message','direct_mention','mention','ambient'],function(bot,message) { - var doorType = message.match[1]; + var doorType = message.match[1]; //match[1] is the (.*) group. match[0] is the entire group (open the (.*) doors). if (doorType === 'back') { return bot.reply(message, 'I cannot do that'); } From 65fb5ca1e6d943b42940f8572d6d438a816d996d Mon Sep 17 00:00:00 2001 From: Rafael Cosman Date: Tue, 29 Dec 2015 01:30:21 +0000 Subject: [PATCH 29/35] Improves regex example and adds S.O.2001 joke --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index e1e13d2ee..f35f01121 100755 --- a/readme.md +++ b/readme.md @@ -339,10 +339,10 @@ controller.hears(['keyword','^pattern$'],['direct_message','direct_mention','men For example, ```javascript -controller.hears('open the (.*) doors',['direct_message','direct_mention','mention','ambient'],function(bot,message) { +controller.hears('open the (.*) doors',['direct_message','mention'],function(bot,message) { var doorType = message.match[1]; //match[1] is the (.*) group. match[0] is the entire group (open the (.*) doors). - if (doorType === 'back') { - return bot.reply(message, 'I cannot do that'); + if (doorType === 'pod bay') { + return bot.reply(message, 'I\'m sorry, Dave. I\'m afraid I can\'t do that.'); } return bot.reply(message, 'Okay'); }); From 449a640d082a2cef3d44fa60870bcb686b6b8017 Mon Sep 17 00:00:00 2001 From: Ben Brown Date: Mon, 28 Dec 2015 21:08:08 -0600 Subject: [PATCH 30/35] adjust method for shutting down bot process to make it friendly with slackbutton bots --- lib/CoreBot.js | 6 ++++++ lib/Slackbot_worker.js | 3 --- test/Slack_web_api-test.js | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/CoreBot.js b/lib/CoreBot.js index f6373bd79..b62f528e3 100755 --- a/lib/CoreBot.js +++ b/lib/CoreBot.js @@ -719,6 +719,12 @@ function Botkit(configuration) { } } + botkit.shutdown = function() { + if (botkit.tickInterval) { + clearInterval(botkit.tickInterval); + } + } + botkit.startTask = function(bot,message,cb) { diff --git a/lib/Slackbot_worker.js b/lib/Slackbot_worker.js index d1bc12602..a3cc0829b 100755 --- a/lib/Slackbot_worker.js +++ b/lib/Slackbot_worker.js @@ -50,9 +50,6 @@ module.exports = function(botkit,config) { } bot.closeRTM = function() { - // set in ./CoreBot - clearInterval(bot.botkit.tickInterval); - // if (bot.rtm) { bot.rtm.close(); } diff --git a/test/Slack_web_api-test.js b/test/Slack_web_api-test.js index b2a7f2c27..b33e5bf3c 100644 --- a/test/Slack_web_api-test.js +++ b/test/Slack_web_api-test.js @@ -18,7 +18,7 @@ test('sanity', t=> { test('can start and then stop a bot', t=> { - var controller = Botkit.slackbot({debug:false}) + var controller = Botkit.slackbot({debug:false}) var bot = controller.spawn(token).startRTM((err, bot, payload)=> { if (err) { @@ -26,14 +26,14 @@ test('can start and then stop a bot', t=> { } else { t.ok(bot, 'got the bot') - console.log(Object.keys(bot), payload) + console.log(Object.keys(bot)) } // does not exit! bot.rtm.terminate() bot.closeRTM() + controller.shutdown() t.end() }) }) - From b7dcf762e7f55e16a55fe4e389946e11a0040e94 Mon Sep 17 00:00:00 2001 From: Ben Brown Date: Mon, 28 Dec 2015 21:15:56 -0600 Subject: [PATCH 31/35] Update syntax for specifying a redirect URI to oauth --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index f35f01121..e5adc5411 100755 --- a/readme.md +++ b/readme.md @@ -968,7 +968,7 @@ See the [included examples](#included-examples) for several ready to use example | Argument | Description |--- |--- -| config | configuration object containing clientId, clientSecret, redirect_uri and scopes +| config | configuration object containing clientId, clientSecret, redirectUri and scopes Configure Botkit to work with a Slack application. @@ -979,7 +979,7 @@ Configuration must include: * clientId - Application clientId from Slack * clientSecret - Application clientSecret from Slack -* redirect_uri - the base url of your application +* redirectUri - the base url of your application * scopes - an array of oauth permission scopes Slack has [_many, many_ oauth scopes](https://api.slack.com/docs/oauth-scopes) @@ -1006,7 +1006,7 @@ var controller = Botkit.slackbot(); controller.configureSlackApp({ clientId: process.env.clientId, clientSecret: process.env.clientSecret, - redirect_uri: 'http://localhost:3002', + redirectUri: 'http://localhost:3002', scopes: ['incoming-webhook','team:read','users:read','channels:read','im:read','im:write','groups:read','emoji:read','chat:write:bot'] }); From 94f6ba6194c6c695f752a37b34fa2dae18746453 Mon Sep 17 00:00:00 2001 From: Ben Brown Date: Mon, 28 Dec 2015 21:32:19 -0600 Subject: [PATCH 32/35] add a negative test case for rtm connects --- test/Slack_web_api-test.js | 41 ++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/test/Slack_web_api-test.js b/test/Slack_web_api-test.js index b33e5bf3c..aaba05ca8 100644 --- a/test/Slack_web_api-test.js +++ b/test/Slack_web_api-test.js @@ -19,6 +19,21 @@ test('sanity', t=> { test('can start and then stop a bot', t=> { var controller = Botkit.slackbot({debug:false}) + t.plan(3); + + controller.on('rtm_open',function(bot) { + t.ok(bot,'rtm_open fired'); + }); + + controller.on('rtm_close',function(bot) { + + t.ok(bot,'disconnected successfully'); + + controller.shutdown() + t.end() + + }); + var bot = controller.spawn(token).startRTM((err, bot, payload)=> { if (err) { @@ -27,13 +42,27 @@ test('can start and then stop a bot', t=> { else { t.ok(bot, 'got the bot') console.log(Object.keys(bot)) - } + bot.closeRTM() - // does not exit! - bot.rtm.terminate() - bot.closeRTM() - controller.shutdown() + } - t.end() }) }) + +test('failed bot properly fails',t=>{ + + var controller = Botkit.slackbot({debug:false}) + var bot = controller.spawn('1231').startRTM((err,bot,payload)=>{ + + if (err) { + t.ok(err,'got an error'); + console.log(err); + } else { + t.fail('Should have errored!','Should have errored!'); + } + controller.shutdown(); + t.end(); + + }); + +}) From b008d5213f9ba6affac44fdeeffde2014ce8c337 Mon Sep 17 00:00:00 2001 From: Ben Brown Date: Mon, 28 Dec 2015 21:35:03 -0600 Subject: [PATCH 33/35] update npm version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5ccc40dec..f551633ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "botkit", - "version": "0.0.4", + "version": "0.0.5", "description": "Building blocks for Building Bots", "main": "lib/Botkit.js", "dependencies": { From 46b2cc167c49e42e2c79fa2c62676857416551b9 Mon Sep 17 00:00:00 2001 From: Guillaume Potier Date: Tue, 22 Dec 2015 00:00:12 +0100 Subject: [PATCH 34/35] added simple Redis storage. Moved storage classes to storages/ subdir --- lib/storage/redis_storage.js | 54 ++++++++++++++++++++++++++++++++++++ readme.md | 16 +++++++++++ 2 files changed, 70 insertions(+) create mode 100644 lib/storage/redis_storage.js diff --git a/lib/storage/redis_storage.js b/lib/storage/redis_storage.js new file mode 100644 index 000000000..0c9d94cc0 --- /dev/null +++ b/lib/storage/redis_storage.js @@ -0,0 +1,54 @@ +var redis = require('redis'); + +/* + * All optionnals + * + * config = { + * namespace: namespace, + * host: host, + * port: port + * } + */ +module.exports = function(config) { + config = config || {}; + config.namespace = config.namespace || 'botkit:store'; + + var storage = {}, + client = redis.createClient(config), // could pass specific redis config here + methods = config.methods || ['teams', 'users', 'channels']; + + // Implements required API methods + for (var i = 0; i < methods.length; i++) { + storage[methods[i]] = function(hash) { + return { + get: function(id,cb) { + client.hget(config.namespace + ':' + hash, id, function (err, res) { + cb(err, JSON.parse(res)); + }); + }, + save: function(object,cb) { + if (!object.id) // Silently catch this error? + return cb(new Error('The given object must have an id property'), {}); + + client.hset(config.namespace + ':' + hash, object.id, JSON.stringify(object), cb); + }, + all: function(cb) { + client.hgetall(config.namespace + ':' + hash, function (err, res) { + if (err) + return cb(err, {}); + + if (null === res) + return cb(err, res); + + for (var i in res) + res[i] = JSON.parse(res[i]); + + cb(err, res); + }); + } + } + }(methods[i]); + } + + return storage; +}; diff --git a/readme.md b/readme.md index e5adc5411..30f36a492 100755 --- a/readme.md +++ b/readme.md @@ -890,7 +890,23 @@ bot.api.channels.list({},function(err,response) { ## Storing Information +<<<<<<< b008d5213f9ba6affac44fdeeffde2014ce8c337 Botkit has a built in storage system used to keep data on behalf of users and teams between sessions. Botkit uses this system automatically when storing information for Slack Button applications (see below). +======= +Botkit has a built in storages systems used to keep data +on behalf of users and teams between sessions. Botkit uses this system automatically when storing information for Slack Button applications (see below). + +By default, Botkit will use [json-file-store](https://github.com/flosse/json-file-store) to keep data in JSON files in the filesystem of the computer where the bot is executed. (Note this will not work on Heroku or other hosting systems that do not let node applications write to the file system.) + +There is also a simple Redis storage to keep data in Redis hashes sets. + +Support for freeform storage for teams, users and channels. +Basically this is a key value store. You can pass in +whatever data you like to any of these, as long as it has an +ID field, which should be a Slack unique id. + +- json-file-store +>>>>>>> added simple Redis storage. Moved storage classes to storages/ subdir By default, Botkit will use [json-file-store](https://github.com/flosse/json-file-store) to keep data in JSON files in the filesystem of the computer where the bot is executed. (Note this will not work on Heroku or other hosting systems that do not let node applications write to the file system.) Initialize this system when you create the bot: ```javascript From 7cb30b96e154c9f41515a4efa2d5d4aa4dff6593 Mon Sep 17 00:00:00 2001 From: Guillaume Potier Date: Tue, 29 Dec 2015 23:02:06 +0100 Subject: [PATCH 35/35] Redis storage | make tests pass + added allById method --- lib/storage/redis_storage.js | 17 +++++++++++++---- lib/storage/storage_test.js | 16 +++++++++++----- package.json | 3 ++- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/lib/storage/redis_storage.js b/lib/storage/redis_storage.js index 0c9d94cc0..601e35a0f 100644 --- a/lib/storage/redis_storage.js +++ b/lib/storage/redis_storage.js @@ -32,7 +32,7 @@ module.exports = function(config) { client.hset(config.namespace + ':' + hash, object.id, JSON.stringify(object), cb); }, - all: function(cb) { + all: function(cb, options) { client.hgetall(config.namespace + ':' + hash, function (err, res) { if (err) return cb(err, {}); @@ -40,11 +40,20 @@ module.exports = function(config) { if (null === res) return cb(err, res); - for (var i in res) - res[i] = JSON.parse(res[i]); + var parsed; + var array = []; - cb(err, res); + for (var i in res) { + parsed = JSON.parse(res[i]); + res[i] = parsed; + array.push(parsed); + } + + cb(err, options && options.type === 'object' ? res : array); }); + }, + allById: function(cb) { + this.all(cb, {type: 'object'}); } } }(methods[i]); diff --git a/lib/storage/storage_test.js b/lib/storage/storage_test.js index 1e3646c59..fb0d7cbfa 100644 --- a/lib/storage/storage_test.js +++ b/lib/storage/storage_test.js @@ -30,7 +30,7 @@ var testStorageMethod = function(storageMethod) { console.log(data); test.assert(data.foo === testObj0.foo); }); - + storageMethod.all(function(err, data) { test.assert(!err); console.log(data); @@ -52,7 +52,13 @@ testStorageMethod(simple_storage.channels); testStorageMethod(simple_storage.teams); // Test mongo_storage -mongo_storage = require('./mongo_storage.js')({mongo_uri: 'mongodb://test:test@ds037145.mongolab.com:37145/slack-bot-test'}); -testStorageMethod(mongo_storage.users); -testStorageMethod(mongo_storage.channels); -testStorageMethod(mongo_storage.teams); +// mongo_storage = require('./mongo_storage.js')({mongo_uri: 'mongodb://test:test@ds037145.mongolab.com:37145/slack-bot-test'}); +// testStorageMethod(mongo_storage.users); +// testStorageMethod(mongo_storage.channels); +// testStorageMethod(mongo_storage.teams); + +// Test redis_storage +redis_storage = require('./redis_storage.js')(); +testStorageMethod(redis_storage.users); +testStorageMethod(redis_storage.channels); +testStorageMethod(redis_storage.teams); diff --git a/package.json b/package.json index f551633ad..47d836e7a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "devDependencies": { "node-env-file": "^0.1.8", "tap-spec": "^4.1.1", - "tape": "^4.2.2" + "tape": "^4.2.2", + "unit.js": "^2.0.0" }, "scripts": { "test": "node test/*-test.js | tap-spec"