slack-helperbot
Version:
A slackbot framework for easily building microbots and commands
204 lines (164 loc) • 5.3 kB
JavaScript
var _ = require('lodash');
var path = require('path');
var SlackBot = require('slackbots');
var Logbot = require('./logbot');
var Storage = require('./storage');
var DEBUG = false;
var LOCAL = false;
var Channels = {};
var Users = {}
var Bots = [];
var botEventMapping = {}
var BotInstance = {};
var BotInfo = {
icon : ':robot_face:',
name : 'helperbot'
}
var getBotInContext = function(bot, eventData){
var botInfo = {
icon_emoji : bot.icon || BotInfo.icon,
username : bot.name || BotInfo.name
}
return {
reply : function(msg, target){
var targ = eventData.channel;
if(eventData.isDirect) targ = eventData.user;
return BotInstance.postTo(target || targ, msg, botInfo)
},
react : function(emoji){
return BotInstance._api('reactions.add', {
name : emoji,
channel : eventData.channelId,
timestamp : eventData.ts
});
},
whisper : function(msg, target){
target = target || eventData.user;
BotInstance.postTo(target, msg, botInfo);
}
}
}
var shouldHelperRespond = function(eventData){
//Don't listen to yourself
if(eventData.user == BotInfo.name) return false;
//Don't ever listen to logbot
if(eventData.user == 'logbot') return false;
//if in production, never listen to #diagnostics
if(!LOCAL && eventData.channel == 'diagnostics') return false;
return true;
}
var shouldBotRespond = function(eventData, bot){
//Don't listen to yourself
if(eventData.user && eventData.user == bot.name) return false;
var shouldRespond = true;
//Unless locally developing, check if the bot is only supposed to listen in a specific channel
if(bot.listenIn) shouldRespond = false;
if(eventData.channel == bot.listenIn || _.includes(bot.listenIn, eventData.channel)) shouldRespond = true;
if(eventData.isDirect && (bot.listenIn == 'direct' || _.includes(bot.listenIn, 'direct'))){
shouldRespond = true
}
return shouldRespond;
}
//Cleans up the event object slack gives us
var enhanceEventData = function(eventData){
eventData.channelId = eventData.channel;
eventData.userId = eventData.user;
//For reactions
if(eventData.item && eventData.item.channel) eventData.channelId = eventData.item.channel;
if(eventData.channelId) eventData.channel = Channels[eventData.channelId];
if(eventData.userId) eventData.user = Users[eventData.userId];
if(eventData.username) eventData.user = eventData.username;
if(eventData.channelId && eventData.channelId[0] == 'D'){
eventData.isDirect = true;
eventData.channel = BotInfo.name;
}
return eventData;
}
var handleEvent = function(data) {
data = enhanceEventData(data);
if(!shouldHelperRespond(data)) return;
//if locally developing, it console logs every event
//if(LOCAL){console.log(data);console.log('---');}
_.each(botEventMapping[data.type], (bot)=>{
if(shouldBotRespond(data, bot)){
var errHandler = function(err){
Logbot.error('Bot Run Error : ' + bot.file, err);
getBotInContext(bot, data).reply('Oops, looks like I broke. Check out #diagnostics for details.');
};
var d = require('domain').create();
d.on('error', errHandler);
d.run(function(){
try{
bot.response(data.text, data, getBotInContext(bot, data), BotInstance);
}catch(err){
errHandler(err);
}
});
}
});
};
var handleStart = function(){
//Populate public Channels
_.each(BotInstance.channels, (channel)=>{
Channels[channel.id] = channel.name;
});
//Populate private Channels
_.each(BotInstance.groups, (channel)=>{
Channels[channel.id] = channel.name;
});
//Popualte Users
_.each(BotInstance.users, (user)=>{
Users[user.id] = user.name;
});
}
module.exports = {
getBots : function(){
return Bots;
},
getBotContext : function(botInfo, defaultInfo){
return getBotInContext(botInfo || {}, defaultInfo || {});
},
start : function(botInfo, isLocal){
LOCAL = isLocal;
BotInstance = new SlackBot({
token : botInfo.token,
name : botInfo.name
});
BotInfo = _.extend(BotInfo, botInfo);
BotInstance.on('start', handleStart);
BotInstance.on('message', handleEvent);
},
load : function(botList){
var rootDir = path.dirname(Object.keys(require.cache)[0]);
var loadResults ={
success : [],
error : []
};
var createDummyBot = ()=>{return {name:BotInfo.name,listenFor:[], response:function(){}}};
Storage.init(function(){
Bots = _.map(botList, function(botPath){
try{
var bot = require(path.join(rootDir, botPath));
bot.path = botPath;
bot.file = path.basename(botPath);
loadResults.success.push(botPath);
bot = _.extend(createDummyBot(), bot); //Add defaults
return bot;
}catch(err){
Logbot.error('Bot Load Error : ' + botPath, err);
if(LOCAL) throw err;
loadResults.error.push(botPath);
return createDummyBot();
}
});
//Create object that maps message types to which bot triggers it
_.each(Bots, function(bot){
_.each(bot.listenFor, function(trigger){
if(!botEventMapping[trigger]) botEventMapping[trigger] = [];
botEventMapping[trigger].push(bot);
})
});
})
return loadResults;
}
}