Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Commit

Permalink
Merge pull request #500 from howdyai/dev/eventsapi
Browse files Browse the repository at this point in the history
Events API support
  • Loading branch information
Ben Brown authored Nov 21, 2016
2 parents 4cc989b + 54cdb17 commit 00323fd
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 157 deletions.
1 change: 1 addition & 0 deletions examples/slackbutton_bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ if (!process.env.clientId || !process.env.clientSecret || !process.env.port || !

var controller = Botkit.slackbot({
json_file_store: './db_slackbutton_bot/',
// rtm_receive_messages: false, // disable rtm_receive_messages if you enable events api
}).configureSlackApp(
{
clientId: process.env.clientId,
Expand Down
1 change: 1 addition & 0 deletions examples/slackbutton_bot_interactivemsg.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ if (!process.env.clientId || !process.env.clientSecret || !process.env.port) {
var controller = Botkit.slackbot({
// interactive_replies: true, // tells botkit to send button clicks into conversations
json_file_store: './db_slackbutton_bot/',
// rtm_receive_messages: false, // disable rtm_receive_messages if you enable events api
}).configureSlackApp(
{
clientId: process.env.clientId,
Expand Down
2 changes: 1 addition & 1 deletion lib/CoreBot.js
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,7 @@ function Botkit(configuration) {
botkit.config = configuration;

/** Default the application to listen to the 0.0.0.0, the default
* for node's http module. Developers can specify a hostname or IP
* for node's http module. Developers can specify a hostname or IP
* address to override this.
**/
if (!botkit.config.hostname) {
Expand Down
275 changes: 187 additions & 88 deletions lib/SlackBot.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@ function Slackbot(configuration) {
// Create a core botkit bot
var slack_botkit = Botkit(configuration || {});

// Set some default configurations unless they've already been set.

// Should the RTM connections ingest received messages
// Developers using the new Events API will set this to false
// This allows an RTM connection to be kept alive (so bot appears online)
// but receive messages only via events api
if (slack_botkit.config.rtm_receive_messages === undefined) {
slack_botkit.config.rtm_receive_messages = true;
}




var spawned_bots = [];

// customize the bot definition, which will be used when new connections
Expand Down Expand Up @@ -120,147 +133,216 @@ function Slackbot(configuration) {
'webhooks at: http://' + slack_botkit.config.hostname + ':' + slack_botkit.config.port + '/slack/receive');
webserver.post('/slack/receive', function(req, res) {

// is this an interactive message callback?
if (req.body.payload) {
// is this an events api url handshake?
if (req.body.type === 'url_verification') {
slack_botkit.debug('Received url handshake');
res.status(200).json({ challenge: req.body.challenge });
return;
}

var message = JSON.parse(req.body.payload);
for (var key in req.body) {
message[key] = req.body[key];
}

// let's normalize some of these fields to match the rtm message format
message.user = message.user.id;
message.channel = message.channel.id;

// put the action value in the text field
// this allows button clicks to respond to asks
message.text = message.actions[0].value;
if (req.body.type === 'event_callback') {
// Receive messages and trigger events from the Events API
return handleEventsAPI(req, res);
} else if (req.body.payload) {
// is this an interactive message callback?
return handleInteractiveMessage(req,res);
} else if (req.body.command) {
// this is a slash command
return handleSlashCommand(req,res);
} else if (req.body.trigger_word) {
return handleOutgoingWebhook(req,res);
}

message.type = 'interactive_message_callback';
});

slack_botkit.findTeamById(message.team.id, function(err, team) {
if (err || !team) {
slack_botkit.log.error('Received interactive message, but could not load team');
} else {
res.status(200);
res.send('');
return slack_botkit;
};

var bot = slack_botkit.spawn(team);
/* Handler functions for the various ways Slack might send a message to
* Botkit via webhooks. These include interactive messages (button clicks),
* events api (messages sent over web hook), slash commands, and outgoing webhooks
* (patterns matched in slack that result in a webhook)
*/
function handleInteractiveMessage(req,res) {
var message = JSON.parse(req.body.payload);
for (var key in req.body) {
message[key] = req.body[key];
}

bot.team_info = team;
bot.res = res;
// let's normalize some of these fields to match the rtm message format
message.user = message.user.id;
message.channel = message.channel.id;

slack_botkit.trigger('interactive_message_callback', [bot, message]);
// put the action value in the text field
// this allows button clicks to respond to asks
message.text = message.actions[0].value;

if (configuration.interactive_replies) {
message.type = 'message';
slack_botkit.receiveMessage(bot, message);
}
}
});
message.type = 'interactive_message_callback';

// this is a slash command
} else if (req.body.command) {
var message = {};
slack_botkit.findTeamById(message.team.id, function(err, team) {
if (err || !team) {
slack_botkit.log.error('Received interactive message, but could not load team');
} else {
res.status(200);
res.send('');

var bot = slack_botkit.spawn(team);

bot.team_info = team;
bot.res = res;

slack_botkit.trigger('interactive_message_callback', [bot, message]);

for (var key in req.body) {
message[key] = req.body[key];
if (configuration.interactive_replies) {
message.type = 'message';
slack_botkit.receiveMessage(bot, message);
}
}
});
}
function handleEventsAPI(req, res) {
// respond to events api with a 200
res.sendStatus(200);

// let's normalize some of these fields to match the rtm message format
message.user = message.user_id;
message.channel = message.channel_id;
var message = {};
for (var key in req.body.event) {
message[key] = req.body.event[key];
}

// Is this configured to use Slackbutton?
// If so, validate this team before triggering the event!
// Otherwise, it's ok to just pass a generic bot in
if (slack_botkit.config.clientId && slack_botkit.config.clientSecret) {
// let's normalize some of these fields to match the rtm message format
message.team = req.body.team_id;
message.events_api = true;
message.authed_users = req.body.authed_users;

slack_botkit.findTeamById(message.team_id, function(err, team) {
if (err || !team) {
slack_botkit.log.error('Received slash command, but could not load team');
} else {
message.type = 'slash_command';
// HEY THERE
// Slash commands can actually just send back a response
// and have it displayed privately. That means
// the callback needs access to the res object
// to send an optional response.
slack_botkit.findTeamById(message.team, function(err, team) {
if (err || !team) {
slack_botkit.log.error('Received Events API message, but could not load team:', err);
return;
} else {
var bot = slack_botkit.spawn(team);
// Identify the bot from either team storage or identifyBot()
bot.team_info = team;
bot.identity = {
id: team.bot.user_id,
name: team.bot.name
};

res.status(200);
if (team.bot.user_id === req.body.event.user) {
slack_botkit.debug('Got event from this bot user, ignoring it');
return;
}
if (bot.identity == undefined || bot.identity.id == null) {
slack_botkit.log.error('Could not identify bot');
return;
} else {
if (req.body.event.type === 'message') {
slack_botkit.receiveMessage(bot, message);
} else {
slack_botkit.trigger(message.type, [bot, message]);
}
}
}
});
};
function handleSlashCommand(req, res) {
var message = {};

var bot = slack_botkit.spawn(team);
for (var key in req.body) {
message[key] = req.body[key];
}

bot.team_info = team;
bot.res = res;
// let's normalize some of these fields to match the rtm message format
message.user = message.user_id;
message.channel = message.channel_id;

slack_botkit.receiveMessage(bot, message);
// Is this configured to use Slackbutton?
// If so, validate this team before triggering the event!
// Otherwise, it's ok to just pass a generic bot in
if (slack_botkit.config.clientId && slack_botkit.config.clientSecret) {

}
});
slack_botkit.findTeamById(message.team_id, function(err, team) {
if (err || !team) {
slack_botkit.log.error('Received slash command, but could not load team');
} else {

message.type = 'slash_command';
// HEY THERE
// Slash commands can actually just send back a response
// and have it displayed privately. That means
// the callback needs access to the res object
// to send an optional response.

var team = {
id: message.team_id,
};

res.status(200);

var bot = slack_botkit.spawn({});
var bot = slack_botkit.spawn(team);

bot.team_info = team;
bot.res = res;

slack_botkit.receiveMessage(bot, message);

}
});
} else {

} else if (req.body.trigger_word) {
message.type = 'slash_command';
// HEY THERE
// Slash commands can actually just send back a response
// and have it displayed privately. That means
// the callback needs access to the res object
// to send an optional response.

var message = {};
var team = {
id: message.team_id,
};

for (var key in req.body) {
message[key] = req.body[key];
}
res.status(200);

var bot = slack_botkit.spawn({});

var team = {
id: message.team_id,
};
bot.team_info = team;
bot.res = res;

// let's normalize some of these fields to match the rtm message format
message.user = message.user_id;
message.channel = message.channel_id;
slack_botkit.receiveMessage(bot, message);

message.type = 'outgoing_webhook';
}
}
function handleOutgoingWebhook(req, res) {
var message = {};

res.status(200);
for (var key in req.body) {
message[key] = req.body[key];
}

var bot = slack_botkit.spawn(team);
bot.res = res;
bot.team_info = team;

var team = {
id: message.team_id,
};

slack_botkit.receiveMessage(bot, message);
// let's normalize some of these fields to match the rtm message format
message.user = message.user_id;
message.channel = message.channel_id;

// outgoing webhooks are also different. They can simply return
// a response instead of using the API to reply. Maybe this is
// a different type of event!!
message.type = 'outgoing_webhook';

}
res.status(200);

});
var bot = slack_botkit.spawn(team);
bot.res = res;
bot.team_info = team;

return slack_botkit;

slack_botkit.receiveMessage(bot, message);

// outgoing webhooks are also different. They can simply return
// a response instead of using the API to reply. Maybe this is
// a different type of event!!
};

/* End of webhook handler functions
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

slack_botkit.saveTeam = function(team, cb) {
slack_botkit.storage.teams.save(team, cb);
};
Expand Down Expand Up @@ -474,6 +556,7 @@ function Slackbot(configuration) {
}

if (auth.bot) {

team.bot = {
token: auth.bot.bot_access_token,
user_id: auth.bot.bot_user_id,
Expand All @@ -500,7 +583,23 @@ function Slackbot(configuration) {
slack_botkit.trigger('update_team', [bot, team]);
}

slack_botkit.storage.users.get(identity.user_id, function(err, user) {
if (team.bot) {
// call auth test on the bot token
// to capture its name
auth_test({
token: team.bot.token
}, function(err, auth_data) {
team.bot.name = auth_data.user;
slack_botkit.saveTeam(team, function(err, id) {
if (err) {
slack_botkit.log.error('An error occurred while saving a team: ', err);
}
});

});
}

slack_botkit.storage.users.get(identity.user_id, function(err, user) {
isnew = false;
if (!user) {
isnew = true;
Expand Down
2 changes: 1 addition & 1 deletion lib/Slackbot_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ module.exports = function(botkit, config) {
* but adds in additional fields for internal use!
* (including the teams api details)
*/
if (message != null) {
if (message != null && bot.botkit.config.rtm_receive_messages) {
botkit.receiveMessage(bot, message);
}
});
Expand Down
Loading

0 comments on commit 00323fd

Please sign in to comment.