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

Events API support #500

Merged
merged 6 commits into from
Nov 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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