From 042dfde3e79afd320843edd9a7a532edae2aa9a1 Mon Sep 17 00:00:00 2001 From: jonchurch Date: Tue, 15 Aug 2017 19:00:46 -0400 Subject: [PATCH] first pass ephem --- docs/readme-slack.md | 71 ++++++++++++++++++++++++++++++++++++++++++ lib/Slack_web_api.js | 6 ++++ lib/Slackbot_worker.js | 59 ++++++++++++++++++++++++++++++++++- 3 files changed, 135 insertions(+), 1 deletion(-) diff --git a/docs/readme-slack.md b/docs/readme-slack.md index bb5ba9f0f..696bc2bb7 100644 --- a/docs/readme-slack.md +++ b/docs/readme-slack.md @@ -220,6 +220,77 @@ bot.startRTM(function(err, bot, payload) { setTimeout(bot.destroy.bind(bot), 10000) ``` +### Ephemeral Messages + +Using the Web API, messages can be sent to a user "ephemerally" which will only show to them, and no one else. Learn more about ephemeral messages at the [Slack API Documentation](https://api.slack.com/methods/chat.postEphemeral). When sending an ephemeral message, you must specify a valid `user` and `channel` id. Valid meaning the specified user is in the specified channel. Currently, updating interactive messages are not supported by ephemeral messages, but you can still create them and listen to the events. They will not have a reference to the original message, however. + +#### Ephemeral Message Authorship + +Slack allows you to post an ephemeral message as either the user you have an auth token for (would be your bot user in most cases), or as your app. The display name and icon will be different accordingly. The default is set to `as_user: true` for all functions except `bot.sendEphemeral()`. Override the default of any message by explicitly setting `as_user` on the outgoing message. + + +#### bot.whisper() +| Argument | Description +|--- |--- +| src | Message object to reply to, **src.user is required** +| message | _String_ or _Object_ Outgoing response +| callback | _Optional_ Callback in the form function(err,response) { ... } + +Functions the same as `bot.reply()` but sends the message ephemerally. Note, src message must have a user field set in addition to a channel + + +`bot.whisper()` defaults to `as_user: true` unless otherwise specified on the message object. This means messages will be attributed to your bot user, or whichever user who's token you are making the API call with. + + +#### bot.sendEphemeral() +| Argument | Description +|--- |--- +| message | _String_ or _Object_ Outgoing response, **message.user is required** +| callback | _Optional_ Callback in the form function(err,response) { ... } + +To send a spontaneous ephemeral message (which slack discourages you from doing) use `bot.sendEphemeral` which functions similarly as `bot.say()` and `bot.send()` + + +```javascript +controller.hears(['^spooky$'], function(bot, message) { + // default behavior, post as the bot user + bot.whisper(message, 'Booo! This message is ephemeral and private to you') +}) + +controller.hears(['^spaghetti$'], function(bot, message) { + // attribute slack message to app, not bot user + bot.whisper(message, {as_user: false, text: 'I may be a humble App, but I too love a good noodle'}) +}) + +controller.on('custom_triggered_event', function(bot, trigger) { + // pretend to get a list of user ids from out analytics api... + fetch('users/champions', function(err, userArr) { + userArr.map(function(user) { + // iterate over every user and send them a message + bot.sendEphemeral({ + channel: 'general', + user: user.id, + text: "Pssst! You my friend, are a true Bot Champion!"}) + }) + }) +}) +``` + +#### Ephemeral Conversations + +To reply to a user ephemerally in a conversation, pass a message object to `convo.say()` `convo.sayFirst()` `convo.ask()` `convo.addMessage()` `convo.addQuestion()` that sets ephemeral to true. + +Currently, you can receive postbacks from interactive messages in ephemeral messages, but cannot update the state of the message. + +```javascript +controller.hears(['^tell me a secret$'], 'direct_mention, ambient, mention', function(bot, message) { + bot.startConversation(message, function(err, convo) { + convo.say('Better take this private...') + convo.say({ ephemeral: true, text: 'These violent delights have violent ends' }) + }) +}) + +``` ### Slack Threads diff --git a/lib/Slack_web_api.js b/lib/Slack_web_api.js index 030197237..789897239 100755 --- a/lib/Slack_web_api.js +++ b/lib/Slack_web_api.js @@ -41,6 +41,7 @@ module.exports = function(bot, config) { 'channels.unarchive', 'chat.delete', 'chat.postMessage', + 'chat.postEphemeral', 'chat.update', 'chat.unfurl', 'dnd.endDnd', @@ -177,6 +178,11 @@ module.exports = function(bot, config) { slack_api.callAPI('chat.postMessage', options, cb); }; + slack_api.chat.postEphemeral = function(options, cb) { + sanitizeOptions(options); + slack_api.callAPI('chat.postEphemeral', options, cb); + }; + slack_api.chat.update = function(options, cb) { sanitizeOptions(options); slack_api.callAPI('chat.update', options, cb); diff --git a/lib/Slackbot_worker.js b/lib/Slackbot_worker.js index 25f1512ab..23094818b 100755 --- a/lib/Slackbot_worker.js +++ b/lib/Slackbot_worker.js @@ -4,7 +4,6 @@ var slackWebApi = require(__dirname + '/Slack_web_api.js'); var HttpsProxyAgent = require('https-proxy-agent'); var Back = require('back'); - module.exports = function(botkit, config) { var bot = { type: 'slack', @@ -338,6 +337,10 @@ module.exports = function(botkit, config) { }; bot.send = function(message, cb) { + if (message.ephemeral) { + bot.sendEphemeral(message, cb) + return + } botkit.debug('SAY', message); /** @@ -413,6 +416,39 @@ module.exports = function(botkit, config) { cb && cb(err); } } + } + bot.sendEphemeral = function (message, cb) { + botkit.debug('SAY EPHEMERAL', message); + + /** + * Construct a valid slack message. + */ + var slack_message = { + type: message.type || 'message', + channel: message.channel, + text: message.text || null, + user: message.user, + as_user: message.as_user || false, + parse: message.parse || null, + link_names: message.link_names || null, + attachments: message.attachments ? + JSON.stringify(message.attachments) : null, + }; + console.log({message}) + console.log({slack_message}) + bot.msgcount++; + + if (!bot.config.token) { + throw new Error('Cannot use web API to send messages.'); + } + + bot.api.chat.postEphemeral(slack_message, function(err, res) { + if (err) { + cb && cb(err); + } else { + cb && cb(null, res); + } + }); }; /** @@ -615,6 +651,27 @@ module.exports = function(botkit, config) { if (src.thread_ts) { msg.thread_ts = src.thread_ts; } + if (msg.ephemeral && ! msg.user) { + msg.user = src.user + msg.as_user = true + } + + bot.say(msg, cb); + }; + + bot.whisper = function(src, resp, cb) { + var msg = {}; + + if (typeof(resp) == 'string') { + msg.text = resp; + } else { + msg = resp; + } + + msg.channel = src.channel; + msg.user = src.user; + msg.as_user = true + msg.ephemeral = true bot.say(msg, cb); };