├── config.json ├── package.json ├── README.md └── yourbot.js /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "clientId":"", 3 | "clientSecret":"", 4 | "token":"", 5 | "port":"", 6 | "userToken":"" 7 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yourbot", 3 | "version": "1.0.0", 4 | "description": "Slackbot", 5 | "main": "yourbot.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "MIT", 12 | "dependencies": { 13 | "botkit": "^0.2.2", 14 | "express": "^4.14.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Interactive-Button-Botkit-Boilerplate 2 | A Slackbot project boilerplate for bots with interactive buttons. 3 | 4 | Read all the docs on botkit @ https://github.com/howdyai/botkit 5 | Read all the Slack API docs @ https://api.slack.com/ (especially the bit on interactive buttons) 6 | 7 | ###This project is all you need to get up and running with interactive messages 8 | 9 | * Works with interactive buttons 10 | * Includes sample 'interactive_message_callback' method 11 | * Includes sample help method 12 | * Includes uptime method 13 | * Includes config file for tokens 14 | 15 | ##To get your App up and running: 16 | * Run an `npm install` 17 | * Make sure you are using a Slack App and have a bot user set 18 | * Plug your tokens and secrets into the config file (Found by managing your App here: https://api.slack.com) 19 | * Make sure you have localtunnel running (https://localtunnel.me/) with the url set in your app credentials under redirect URI. (https://api.slack.com -> https://yoursubdomain.localtunnel.me/oauth) 20 | * Make sure you have your Request URL for interactive messages set to https://yoursubdomain.localtunnel.me/slack/receive 21 | * Run your bot with "node yourbot.js" 22 | * Hit the URL "https://yoursubdomain.localtunnel.me/login" to add your bot to a team 23 | * Direct message your bot "test button" to make sure buttons are working 24 | * Invite your bot to a channel and have fun! 25 | 26 | Created by Christian Hapgood 27 | -------------------------------------------------------------------------------- /yourbot.js: -------------------------------------------------------------------------------- 1 | /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 | Example Slack botkit project 3 | 4 | Read all the docs on botkit @ https://github.com/howdyai/botkit 5 | Read all the Slack API docs @ https://api.slack.com/ (especially the bit on interactive buttons) 6 | 7 | This project is all you need to get up and running with interactive messages 8 | 9 | * Works with interactive buttons 10 | * Includes sample 'interactive_message_callback' method 11 | * Includes sample help method 12 | * Includes uptime method 13 | * Includes config file for tokens 14 | * Includes a method to get cat gifs on demand. 15 | 16 | To get your App up and running: 17 | *Make sure you are using a Slack App and have a bot user set 18 | *Plug your tokens and secrets into the config file (Found by managing your App here: https://api.slack.com) 19 | *Make sure you have localtunnel running with the url set in your app credentials under redirect URI. (https://api.slack.com -> https://yoursubdomain.localtunnel.me/oauth) 20 | *Make sure you have your Request URL for interactive messages set to https://yoursubdomain.localtunnel.me/slack/receive 21 | *Run your bot with "node yourbot.js" 22 | *Hit the URL "https://yoursubdomain.localtunnel.me/login" to add your bot to a team 23 | *Direct message your bot "test button" to make sure buttons are working 24 | *Invite your bot to a channel and have fun! 25 | 26 | Created by Christian Hapgood 27 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 28 | 29 | // Have a remote json config file for holding tokens. add this file to your gitignore. 30 | var config = require('./config.json'); 31 | process.env.token = config.token; 32 | process.env.clientId = config.clientId; 33 | process.env.clientSecret = config.clientSecret; 34 | process.env.port = config.port; 35 | 36 | if (!process.env.token) { 37 | console.log('Error: Specify token in environment'); 38 | process.exit(1); 39 | } 40 | 41 | var Botkit = require('./node_modules/botkit/lib/Botkit.js'); 42 | var os = require('os'); 43 | var express = require('express'); 44 | // Using a token to get user information. Generate a token here https://api.slack.com/docs/oauth-test-tokens 45 | var userToken = config.userToken; 46 | 47 | // Check for ENV variables - Required to be a slack app to use interactive buttons 48 | if (!process.env.clientId || !process.env.clientSecret || !process.env.port) { 49 | console.log('Error: Specify clientId clientSecret and port in environment'); 50 | process.exit(1); 51 | } 52 | 53 | // Sample controller config - REQUIRED FOR INTERACTIVE BUTTONS 54 | var controller = Botkit.slackbot({ 55 | debug: false, 56 | interactive_replies: true, // tells botkit to send button clicks into conversations 57 | json_file_store: './db_slackbutton_bot/', 58 | }).configureSlackApp( 59 | { 60 | clientId: process.env.clientId, 61 | clientSecret: process.env.clientSecret, 62 | // Set scopes as needed. https://api.slack.com/docs/oauth-scopes 63 | scopes: ['bot','incoming-webhook','team:read','users:read','users.profile:read','channels:read','im:read','im:write','groups:read','emoji:read','chat:write:bot'], 64 | } 65 | ); 66 | 67 | // Setup for the Webserver - REQUIRED FOR INTERACTIVE BUTTONS 68 | controller.setupWebserver(process.env.port,function(err,webserver) { 69 | controller.createWebhookEndpoints(controller.webserver); 70 | 71 | controller.createOauthEndpoints(controller.webserver,function(err,req,res) { 72 | if (err) { 73 | res.status(500).send('ERROR: ' + err); 74 | } else { 75 | res.send('Success!'); 76 | } 77 | }); 78 | }); 79 | 80 | // Method for when the bot is added to a team 81 | controller.on('create_bot',function(bot,config) { 82 | if (_bots[bot.config.token]) { 83 | // already online! do nothing. 84 | } else { 85 | bot.startRTM(function(err) { 86 | if (!err) { 87 | trackBot(bot); 88 | } 89 | bot.startPrivateConversation({user: config.createdBy},function(err,convo) { 90 | if (err) { 91 | console.log(err); 92 | } else { 93 | convo.say('I am a bot that has just joined your team'); 94 | convo.say('You must now /invite me to a channel so that I can be of use!'); 95 | } 96 | }); 97 | }); 98 | } 99 | }); 100 | 101 | // Handle events related to the websocket connection to Slack 102 | controller.on('rtm_open',function(bot) { 103 | console.log('** The RTM api just connected!'); 104 | }); 105 | 106 | controller.on('rtm_close',function(bot) { 107 | console.log('** The RTM api just closed'); 108 | // you may want to attempt to re-open 109 | }); 110 | 111 | // just a simple way to make sure we don't 112 | // connect to the RTM twice for the same team 113 | var _bots = {}; 114 | function trackBot(bot) { 115 | _bots[bot.config.token] = bot; 116 | } 117 | 118 | // Controller to test the interative button setup 119 | controller.hears(['test button'], 'direct_message,direct_mention,mention', function (bot, message) { 120 | var testButtonReply = { 121 | username: 'Button Bot' , 122 | text: 'This is a test message with a button', 123 | replace_original: 'true', 124 | attachments: [ 125 | { 126 | fallback: "fallback text", 127 | callback_id: '123', 128 | attachment_type: 'default', 129 | title: 'message title', 130 | text: 'message content', 131 | color: '#0075C7', 132 | actions: [ 133 | { 134 | "name": "button name", 135 | "text": "button text", 136 | "type": "button", 137 | "value": "whatever you want to pass into the interactive_message_callback"} 138 | ] 139 | } 140 | ], 141 | icon_url: 'http://14379-presscdn-0-86.pagely.netdna-cdn.com/wp-content/uploads/2014/05/ButtonButton.jpg' 142 | 143 | } 144 | bot.reply(message, testButtonReply); 145 | }); 146 | 147 | // Sample uptime method 148 | controller.hears(['uptime', 'identify yourself', 'who are you', 'what is your name'], 149 | 'direct_message,direct_mention,mention', function (bot, message) { 150 | var hostname = os.hostname(); 151 | var uptime = formatUptime(process.uptime()); 152 | bot.reply(message, 153 | ':robot_face: I am a bot named <@' + bot.identity.name + 154 | '>. I have been running for ' + uptime + ' on ' + hostname + '.'); 155 | }); 156 | 157 | // Function to format data for the uptime method 158 | function formatUptime(uptime) { 159 | var unit = 'second'; 160 | if (uptime > 60) { 161 | uptime = uptime / 60; 162 | unit = 'minute'; 163 | } 164 | if (uptime > 60) { 165 | uptime = uptime / 60; 166 | unit = 'hour'; 167 | } 168 | if (uptime != 1) { 169 | unit = unit + 's'; 170 | } 171 | 172 | uptime = uptime + ' ' + unit; 173 | return uptime; 174 | } 175 | 176 | // Sample help controller method 177 | controller.hears(['^help[ ]?(.*)'], 'direct_message,direct_mention', function (bot, message) { 178 | var topic = message.match[1]; 179 | 180 | switch(topic) { 181 | case "weather": 182 | bot.reply(message, 'If you had a command to get you the weather you could explain it here'); 183 | break; 184 | case "cat": 185 | bot.reply(message, 'Type "get me a cat" to get a gif of a kitty'); 186 | break; 187 | case "room status": 188 | bot.reply(message, 'This would display info about the "room status" command'); 189 | break; 190 | case "uptime": 191 | bot.reply(message, 'Displays the bot uptime and host'); 192 | break; 193 | default: 194 | bot.reply(message, 'You might want to have a list of all the commands here'); 195 | } 196 | 197 | }); 198 | 199 | //REQUIRED FOR INTERACTIVE BUTTONS 200 | // This controller method handles every interactive button click 201 | controller.on('interactive_message_callback', function(bot, message) { 202 | // These 3 lines are used to parse out the id's 203 | var ids = message.callback_id.split(/\-/); 204 | var user_id = ids[0]; 205 | var item_id = ids[1]; 206 | 207 | var callbackId = message.callback_id; 208 | 209 | // Example use of Select case method for evaluating the callback ID 210 | // Callback ID 123 for weather bot webcam 211 | switch(callbackId) { 212 | case "123": 213 | bot.replyInteractive(message, "Button works!"); 214 | break; 215 | // Add more cases here to handle for multiple buttons 216 | default: 217 | // For debugging 218 | bot.reply(message, 'The callback ID has not been defined'); 219 | } 220 | }); 221 | 222 | //REQUIRED FOR INTERACTIVE MESSAGES 223 | controller.storage.teams.all(function(err,teams) { 224 | 225 | if (err) { 226 | throw new Error(err); 227 | } 228 | 229 | // connect all teams with bots up to slack! 230 | for (var t in teams) { 231 | if (teams[t].bot) { 232 | controller.spawn(teams[t]).startRTM(function(err, bot) { 233 | if (err) { 234 | console.log('Error connecting bot to Slack:',err); 235 | } else { 236 | console.log(bot); 237 | trackBot(bot); 238 | } 239 | }); 240 | } 241 | } 242 | 243 | }); 244 | --------------------------------------------------------------------------------