Name: slack-robot
Owner: Auth0
Description: Simple robot for your slack integration
Forked from: traveloka/slack-robot
Created: 2016-05-10 00:50:25.0
Updated: 2017-10-20 02:52:27.0
Pushed: 2017-08-15 18:07:31.0
Size: 195
Language: JavaScript
GitHub Committers
User | Most Recent Commit | # Commits |
---|
Other Committers
User | Most Recent Commit | # Commits |
---|
Simple robot for your slack integration
slack-robot
initially built to respond message from user with an action, then replying
with a response. While hubot will do the job, it's too generic (no slack specific response like reaction, snippet, attachment, or listening to slack specific event). Using hubot also mean learning hubot specific command, using the right adapter, using the right slack-client, and patching them with slack specific event
While modularity is good, we're not sure about using multiple module with exponentially larger bug possibility (and also broader learning material). Because we are using it in critical application, we need to make sure that the API surface is as little as possible, with near perfect test coverage. With that in mind, we built slack-robot by wrapping “official” slack-client into much easier to consume request-response handling library.
m install slack-robot --save
SlackRobot = require('slack-robot');
robot = new SlackRobot(process.env.SLACK_TOKEN);
ill post 'world' text as bot when receiving 'hello' message
n channel, group, or direct message
t.listen('hello', function (req, res) {
turn res.text('world').send();
gnore message from '#general' channel, even if it matches the listener
t.ignore('#general');
tart listening
t.start();
You can also listen to dynamic message by using parameterized message (usually called named-regexp), by using :name(REGEXP)
syntaxes. All parameters will be available via req.params
end 'get sheep from 2010' to your bot
t.listen('get :animal([a-z\-]+) from :year([0-9]{4})', function (req, res) {
nsole.log(req.params)
{ animal: 'sheep', year: 2010 }
Aside from named regexp (which uses a partial regexp match), you can use pure regexp inside your listener. The difference is, instead of getting an object in req.params
, you get Array of regexp matches in req.matches
end 'get sheep from 2010' to your bot
t.listen(/get ([a-z]+) from ([0-9]{4})/, function (req, res) {
you don't have anything in named-param
nsole.log(req.params)
{}
you use req.matches instead
nsole.log(req.matches);
['sheep', '2010']
In slack-robot
, receiving and sending message is handled via Request
and Response
object. Every time your bot receive a chat, you get Request
object with typedef below
Request = {
ssage: {
type: string
value: {}
om: {
id: string,
name: string
: {
id: string,
type: string, (channel, group, or dm)
?name: string // missing if direct message
rams: {},
tches: []
For example when you @anonymous
send the bot @hola
message get sheep from 2010 @hola
in #general
channel
t.listen('get :animal([a-z\-]+) from :year([0-9]{4})', function (req, res) {
nsole.log(req);
message: {
type: 'text'
value: {
text: 'get sheep from 2010'
mentioned: true
}
},
from: {
id: 'your_random_id',
name: 'anonymous'
},
to: {
id: 'random_channel_id',
name: 'general',
type: 'channel'
},
params: {
animal: 'sheep',
year: '2010',
},
matches: []
To respond a message, use res
object. You can respond multiple times as you want
t.listen('yo', function (req, res) {
*
Send text
@param {string} text
/
s.text('what\'s up?');
*
Send attachment
@param {string} text in attachment
@param {Array<Object>|Object}
@see https://api.slack.com/docs/attachments
/
s.attachment(text, attachment);
*
Send file
@param {string} filename
@param {string|ReadStream} content
@see https://nodejs.org/api/fs.html
/
s.upload('snippet.txt', fs.createReadStream('snippet.txt'));
*
Add reaction to the message
@param {string} reaction emoji
/
s.reaction(':+1:');
*
Always end your handler by returning res.send
/
turn res.send();
Until you call res.send()
, your message will not be sent. By calling
res.send()
, it will queue all your response and send them in series. To change this behavior,
change concurrency
property from robot:
end 3 response in parallel (this will affect all listener)
eep in mind that enabling concurrency means the order of the
essage is not guaranteed
t.set('concurrency', 3);
To respond in another channel/im, simply pass the last argument to .text()
,
.attachment()
or .upload()
, with channel name #general
, group name private-group
,
or user name @anon
, or use an array of string to send multiple target:
t.listen('yo', function (req, res) {
s.attachment('here', attachment, '#general');
s.upload('document.doc', file, '@anon');
s.text('done!', ['#general', '@anon']);
end your request
turn res.send();
NOTE: You cannot use custom response target when adding reaction
Sometimes you want to do some asynchronous task before sending back response, you can
use res.async()
which accept a callback that receive send
function as argument.
To end your asynchronous task call send()
without any argument. If your asynchronous
task failed, call send()
with an error object:
t.listen('deploy', function (req, res) {
s.text('executing scripts..');
turn res.async(function (done) {
childProcess.exec('~/scripts/deploy --to production', function (err, stdout, stderr) {
if (err) {
// return to stop code for reaching res.text
return done(err);
}
// use res.text like usual
res.text('done, printing stdout:');
res.text(stdout);
done();
});
call .send() to send all previous response declared in asynchronous task
.send();
NOTE: Calling res.send()
after res.async()
doesn't send all the response,
because res.send()
is synchronous. Make sure you call res.async().send()
to send the response
If you already use Promise, you can return your Promise chain instead of using
res.async
s2015 code style
t.listen('deploy', (req, res) => {
turn deployer().then(output => {
res.text('done, printing output:');
res.text(output);
return res.send();
You can also send message without having to listen to any message. This is particularly
useful when combined with another service that run asynchronously (for example error
reporting). Use robot.to()
to get the response object
you usually use when responding message
ws = require('websocket');
n('message', function (msg) {
robot.to() is asynchronous by nature because we need to make sure
the bot is connected before you able to send the message
hence the use of the callback to get the response object
bot.to('@anon', function (res) {
res.text('Hi anon, you got a message');
res.text(msg);
return res.send();
;
NOTE: .reaction() and .async() cannot be used here
slack-robot will emit event if something happened. Below is the list of events you can listen to:
message_no_sender
, when you receive a message without user informationown_message
, when you receive a message from bot itselfignored_channel
, when you receive a message in channel that you ignore via robot.ignore
no_listener_match
, when you receive a message without matching listenerresponse_failed
, when failed sending a single responserequest_handled
, when a request has been handlederror
, general error, usually if your listener callback has uncaught exceptionTo listen specific event, use robot.on(message, callback)
. Most event will receive
message object, except response_failed
and error
event which receive error object
instead, request_handled
which receive request object, and message_no_sender
which receive original message object from slack API
t.on('error', function (err) {
print to stderr, or sent to error reporting service
nsole.error(err);
When you have many listener, you sometimes forget all your listeners. You can see it
by enabling help generator which will sent you all your listeners. Enable it using robot.set('help_generator', true)
(it's disabled by default). It will add another listener that will listen to all text message containing “help”. So if you send message
to the bot “show help” or “help”, it will send you the command list.
MIT