GithubHelp home page GithubHelp logo

slackapi / bolt-js Goto Github PK

View Code? Open in Web Editor NEW
2.7K 40.0 377.0 10.98 MB

A framework to build Slack apps using JavaScript

Home Page: https://slack.dev/bolt-js

License: MIT License

TypeScript 97.83% JavaScript 2.17%
slack framework typescript slack-apps javascript socket-mode websocket websockets websocket-client

bolt-js's Introduction

Bolt Bolt logo for JavaScript

codecov Node.js CI

A JavaScript framework to build Slack apps in a flash with the latest platform features. Read the getting started guide to set-up and run your first Bolt app.

Read the documentation to explore the basic and advanced concepts of Bolt for JavaScript.

Setup

npm install @slack/bolt

Initialization

Create an app by calling the constructor, which is a top-level export.

const { App } = require('@slack/bolt');

const app = new App({
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  token: process.env.SLACK_BOT_TOKEN,
});

/* Add functionality here */

(async () => {
  // Start the app
  await app.start(process.env.PORT || 3000);

  console.log('⚡️ Bolt app is running!');
})();

Listening for events

The Slack Request URL for a Bolt app must have the path set to /slack/events.
For example: https://my-slack-app.example.com/slack/events.
Otherwise, all incoming requests from Slack won't be handled.

Apps typically react to a collection of incoming events, which can correspond Events API events, actions, shortcuts, slash commands or options requests. For each type of request, there's a method to build a listener function.

// Listen for an event from the Events API
app.event(eventType, fn);

// Convenience method to listen to only `message` events using a string or RegExp
app.message([pattern ,] fn);

// Listen for an action from a Block Kit element (buttons, select menus, date pickers, etc)
app.action(actionId, fn);

// Listen for dialog submissions
app.action({ callback_id: callbackId }, fn);

// Listen for a global or message shortcuts
app.shortcut(callbackId, fn);

// Listen for slash commands
app.command(commandName, fn);

// Listen for view_submission modal events
app.view(callbackId, fn);

// Listen for options requests (from select menus with an external data source)
app.options(actionId, fn);

Making things happen

Most of the app's functionality will be inside listener functions (the fn parameters above). These functions are called with a set of arguments.

Argument Description
payload Contents of the incoming event. The payload structure depends on the listener. For example, for an Events API event, payload will be the event type structure. For a block action, it will be the action from within the actions array. The payload object is also accessible via the alias corresponding to the listener (message, event, action, shortcut, view, command, or options). For example, if you were building a message() listener, you could use the payload and message arguments interchangeably. An easy way to understand what's in a payload is to log it, or use TypeScript.
say Function to send a message to the channel associated with the incoming event. This argument is only available when the listener is triggered for events that contain a channel_id (the most common being message events). say accepts simple strings (for plain-text messages) and objects (for messages containing blocks). say returns a promise that will resolve with a chat.postMessage response.
ack Function that must be called to acknowledge that an incoming event was received by your app. ack exists for all actions, shortcuts, view, slash command and options requests. ack returns a promise that resolves when complete. Read more in Acknowledging events
client Web API client that uses the token associated with that event. For single-workspace installations, the token is provided to the constructor. For multi-workspace installations, the token is returned by the authorize function.
respond Function that responds to an incoming event if it contains a response_url (actions, shortcuts, view submissions, and slash commands). respond returns a promise that resolves with the results of responding using the response_url.
context Event context. This object contains data about the event and the app, such as the botId. Middleware can add additional context before the event is passed to listeners.
body Object that contains the entire body of the request (superset of payload). Some accessory data is only available outside of the payload (such as trigger_id and authorizations).

The arguments are grouped into properties of one object, so that it's easier to pick just the ones your listener needs (using object destructuring). Here is an example where the app sends a simple response, so there's no need for most of these arguments:

// Reverse all messages the app can hear
app.message(async ({ message, say }) => {
  // Filter out message events with subtypes (see https://api.slack.com/events/message)
  if (message.subtype === undefined || message.subtype === 'bot_message') {
    const reversedText = [...message.text].reverse().join("");
    await say(reversedText);
  }
});

Calling the Web API

In addition to the client property passed to listeners, each app has a top-level client that can be used to call methods. Unlike the client passed to listeners, the top-level client must be passed a token. Read the documentation for more details.

Acknowledging events

Some types of events need to be acknowledged in order to ensure a consistent user experience inside the Slack client (web, mobile, and desktop apps). This includes all action, shortcut, view, command, and options requests. Listeners for these events need to call the ack() function, which is passed in as an argument.

In general, the Slack platform expects an acknowledgement within 3 seconds, so listeners should call this function as soon as possible.

Depending on the type of incoming event a listener is meant for, ack() should be called with a parameter:

  • Block actions, global shortcuts, and message shortcuts: Call ack() with no parameters.

  • View submissions: Call ack() with no parameters or with a response action.

  • Options requests: Call ack() with an object containing the options for the user to see.

  • Legacy message button clicks, menu selections, and slash commands: Either call ack() with no parameters, a string to to update the message with a simple message, or an object to replace it with a complex message. Replacing the message to remove the interactive elements is a best practice for any action that should only be performed once.

  • Events API events do not need an ack() function since they are automatically acknowledged by your app.

Getting Help

The documentation has more information on basic and advanced concepts for Bolt for JavaScript.

If you otherwise get stuck, we're here to help. The following are the best ways to get assistance working through your issue:

  • Issue Tracker for questions, bug reports, feature requests, and general discussion related to Bolt for JavaScript. Try searching for an existing issue before creating a new one.
  • Email our developer support team: [email protected]

Contributing

We welcome contributions from everyone! Please check out our Contributor's Guide for how to contribute in a helpful and collaborative way.

bolt-js's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bolt-js's Issues

stripDirectMention fails when text undefined

/usr/src/app/node_modules/slapp/src/message.js:441
let match = text.match(new RegExp(`^<@${this.meta.bot_user_id}>:{0,1}(.*)`))
^
TypeError: Cannot read property 'match' of undefined
at Message.stripDirectMention (/usr/src/app/node_modules/slapp/src/message.js:441:23)
at fn (/usr/src/app/node_modules/slapp/src/slapp.js:396:24)
at next (/usr/src/app/node_modules/slapp/src/slapp.js:207:13)
at convoStore.get (/usr/src/app/node_modules/slapp/src/slapp.js:115:9)
at kv.get (/usr/src/app/node_modules/slapp-convo-beepboop/src/convoStore.js:54:16)
at /usr/src/app/node_modules/beepboop-persist/index.js:44:9
at /usr/src/app/node_modules/beepboop-persist/lib/beepboop-provider.js:29:44
at finish (/usr/src/app/node_modules/wreck/lib/index.js:214:20)
at wrapped (/usr/src/app/node_modules/hoek/lib/index.js:871:20)
at ClientRequest.onResponse (/usr/src/app/node_modules/wreck/lib/index.js:161:20)

slapp.msg() with embedded 'confirm' doesn't appear to support a callback for 'Cancelled'

Hi @selfcontained I couldn't find a better way to contact you. I am working on a slack bot using the Slapp API. I have a msg.say() that uses an embedded 'confirm'. There is support for a 'callback_id' that gets invoked when the user clicks on 'Ok', but I can't find a way to have an event associated to the 'Cancel' button being pressed. I apologize if this isn't the right forum to ask this question. I scoured through the documentation, but couldn't find any answer.

Is there a way to do this in Slapp?

Here is an example: (Sorry about the formatting)

 msg
        .say({
          text: '',
          attachments: [
            {
              text: 'You have selected to do something',
              fallback: 'Are you sure?',
              callback_id: 'doActualThing',
              actions: [
                {
                  name: 'answer',
                  text: 'Go',
                  type: 'button',
                  value: 'yes',
                  confirm: {
                    title: 'Are you absolutely sure?',
                    text: 'You have selected to do something?',
                    ok_text: 'Yes',
                    dismiss_text: 'No',
                  },
                },
              ],
            }],
        });
    });

   // handler for doing the actual thing
    slapp.action('doActualThing', (msg) => {
      const answer = msg.body.actions[0].value;
      if (answer === 'yes') {
         // do something here
        });
      }
    });
 

Serverless architecture

I was wondering if there's any value in using slapp in a serverless architecture.

From what I've seen in the code samples, it appears it's better fit for the traditional model of a long running service which registers callbacks to the slack API.

How do you get the Message object of a response?

How do you get a Message object of a response from doing msg.say('my response')?

I want to create an attachment after a user clicks confirm in an interactive message and then post it for everyone to see and then start a thread.

When I receive the Message object is of type action, based on that action I create a response. How do I automatically thread that response?

slapp.action('callback', 'post', (msg, val) => {
  response_msg = msg.say('my response').getResponse()
  response_msg.thread().say('this is in the thread')
})

It would be great if a getResponse() method existed so I can thread a say from posting with an action message type

Proper way to get req from msg?

I want to access http req object related to particular slapp msg.
There is 2 ways I found:

  • msg._response.req - wonky and breaks encapsulation. Will not work if _response object will be cleared by msg.clearResponse().
  • Extend meta with req inside context resolver function - looks ok, but I wonder if there is more standard way to do that.

Conversation Queue & Bot Initiate Conversation

So I'm working on a bot in which its possible that a user might get in a conversation with a bot, then in the middle of that conversation, another user could cause the bot to open up a new conversation with the same user.

In this situation, the behavior I'm shooting for is that the second conversation thread would be queued up and run only when the user has finished the first conversation. However, what it does right now is opens up both conversations and attempts to run them in parallel, which is quite confusing and leads to bugs.

Also happy to contribute to making this work, just wanted to put in on the map and see what you guys think about the idea.

I should also note that at the moment the tooling for getting a bot to open a conversation with a user is entirely nonexistent. We just hacked this functionality together by taking an existing Message object and rewriting the username that it is being sent to. Having a cleaner, framework-supported method for having a bot initiate a conversation might be a prerequisite to getting this issue solved, and also I think would be incredibly useful in general 😁

Demos and README don't work

It appears that the demos in the README file no longer work as every time I click on them I just get Not Found.

mention and direct_mention collisions

Currently, the following message returns true for isDirectMention() and isMention(). My suggestion is that isMention() should only return true if there was at least one character before the <@bot-id> pattern.

"@bot hi"

I had the following handlers that resulted in the mention consuming the help handler:

app
  .message(/.*/, 'mention', (msg) => {
    msg.say('You really do care about me. :heart:')
  })
  .message('help', ['direct_message', 'direct_mention'], (msg) => {
    let help = `I will respond to the following messages:
\`bot hi\` for a simple message.
\`bot attachment\` to see a Slack attachment message.
\`@<your bot's name>\` to demonstrate detecting a mention.
\`bot help\` to see this again.`

    msg.say(help)
  })

I would expect this to only match isDirectMention(). If we change isMention() to expect at least one character before the <@bot-id> pattern, this would solve it. Adding ^.+ to the beginning of the regex would do the trick.

isMention () {
  return this.isMessage() && new RegExp(`^.+<@${this.meta.bot_user_id}>`, 'i').test(this.body.event.text)
}

Thoughts? Is this a valid expectation, or working as intended?

Allow RegExp-match for action callback-ids

Allow a RegExp-match for action call-back-ids in slapp.action just like in slapp.event.

Currently the callbackId parameter of the slapp.action function will only accept a simple string but it would be nice to allow a RegExp-match in order to catch multiple action callbacks at once. This is especially useful when multiple action callbacks have a very similar structure but vary in just a few details.

team_join event cannot be processed

Upon receiving a team_join event. I get the following error with slapp

[   2016-08-30T21:26:11Z    ]   slapp:info [evt] tm=T1JUWU5GT ch=C1K0MD53N usr=U26JQCAG2 message.channel_join <@U26JQCAG2|robot2.curtis> has joined the channel
[   2016-08-30T21:26:11Z    ]   slapp:error Cannot process message because the following properties are missing from message.meta: app_token

Attach to event source other than express

I would like to use Slapp in a serverless environment. In doing so, I'm not actually ever running a real http server, all I have is the data from Slack's event api.

This would be fairly easy to hook up by just calling slapp.reciever.emit("message", msg) after building it, but I would like to have a supported way to pass in data and get it into slapp.

I imagine this would look like:

function eventReciever(data) {
    slapp.sendEvent(data)
}

This would take care of calling context, building the Message and sending it through Slapp.

Getting Started / Slapp Example

This isn't an issue and more of a general question as I was looking through past questions and on the web and couldn't find anything. I'm trying to find a tutorial / an example slack app that uses the features of this package, but it seems that every app I find relies on BeepBoop which no longer exists, I was wondering if anyone would able to provide any suggestions.

Passing along state to Interactive Messages handlers

Hi,

I used the starter-slapp-app as a bootstrap project for my bot. It really works fine and wanted to give you a big round of applause for the good work 👍 .

I was simply wondering if there was a way to pass along a state to interactive messages handlers.

Here is some code:
The interactive message I want to send with the state:

// This route is invoked previously where state is initialized.
slapp.route('handle_match_confirmation', (msg, state, duration) => {
  msg
  .say({
    channel: state.loserId,
    text: '',
    attachments: [{
      fallback: 'Match log confirmation',
      title: `Do you confirm that you lost ${state.winnerScore}-${state.loserScore} against <@${state.winnerId}> ?`,
      callback_id: 'match_confirmation_callback',
      color: '#3AA3E3',
      attachment_type: 'default',
      actions: [{
        name: 'match_confirmation',
        text: 'Yep, good game.',
        style: 'primary',
        type: 'button',
        value: 'yes'
      },
      {
        name: 'match_confirmation',
        text: 'NO WAY ! That\' a lie!',
        type: 'button',
        value: 'no'
      }]
    }]
  });
});

My handlers supposed to know the state:

slapp.action('match_confirmation_callback', 'match_confirmation', 'yes', (msg, value) => {
  msg.respond('Too bad, maybe next time :wink:!')
});
slapp.action('match_confirmation_callback', 'match_confirmation', 'no', (msg, value) => {
  msg.respond('What what what ? Someone is trying to cheat then. You need to see this IRL!')
});

The reason I want to pass the state along is because it contains slack_ids I use to send messages + other information regarding the business logic.

Thank you ;)

Add middleware for outgoing messages

Processed for msg.say and msg.respond, not sure what the API looks like yet but would be nice to be able to msg.say({ key , params}) and have it look that up in a language file

Typescript

Hey guys,

While this framework seems awesome, it feels like it would be a lot better with Typescript typings.
Is this something planned ?

Slapp direct_mention/mention not responding back

Hi I was looking at #60 and I added that to my "Subscribe to Bot Events":

screen shot 2017-11-01 at 10 55 03 am

But I can't get a response from: @slapptest hello in my #general channel. I can however get a response from direct_message.

This is my Slapp app:

const Slapp = require('slapp');
const express = require('express');

const {
  SLACK_CLIENT_VERIFICATION_TOKEN: verify_token,
  SLACK_OAUTH_ACCESS_TOKEN: app_token,
  SLACK_CLIENT_ID: app_user_id,
  SLACK_BOT_OAUTH_ACCESS_TOKEN: bot_token,
  PORT: port
} = process.env;

const slapp = Slapp({
  context(req, res, next) {
    req.slapp.meta = Object.assign({}, req.slapp.meta, {
      verify_token,
      app_token,
      app_user_id,
      bot_token
    });
    return next();
  },
  log: true,
  colors: true
});

const app = slapp.attachToExpress(express());

slapp.message(
  '.*',
  ['direct_mention', 'mention', 'direct_message'],
  (msg, text) => {
    msg.say('How are you');
  }
);

console.log(`Listening on port: ${port}`);
app.listen(port);

Am I missing something?

User status change results in invalid user_id from meta.

If you subscribe to the user_change https://api.slack.com/events/user_change event it sends a slack event to the event handler that has the req.body type similar to this (personal info x'd out):

{
  "token": "xxxxxxxxxxx",
  "team_id": "xxxxxxxxxxx",
  "api_app_id": "xxxxxxxxxxx",
  "event": {
    "type": "user_change",
    "user": {
      "id": "xxxxxxxxxxx",
      "team_id": "xxxxxxxxxxx",
      "name": "xxxxxxxxxxx",
      "deleted": false,
      "status": null,
      "color": "9f69e7",
      "real_name": "xxxxxxxxxxx",
      "tz": "xxxxxxxxxxx",
      "tz_label": "xxxxxxxxxxx",
      "tz_offset": -14400,
      "profile": {
        "first_name": "xxxxxxxxxxx",
        "last_name": "xxxxxxxxxxx",
        "avatar_hash": "ged1089dfefe",
        "status_text": "test my status now",
        "status_emoji": ":speech_balloon:",
        "real_name": "xxxxxxxxxxx",
        "real_name_normalized": "xxxxxxxxxxx",
        "email": "xxxxxxxxxxx",
        "image_24": "xxxxxxxxxxx",
        "image_32": "xxxxxxxxxxx",
        "image_48": "xxxxxxxxxxx",
        "image_72": "xxxxxxxxxxx",
        "image_192": "xxxxxxxxxxx",
        "image_512": "xxxxxxxxxxx",
        "fields": null
      },
      "is_admin": true,
      "is_owner": true,
      "is_primary_owner": true,
      "is_restricted": false,
      "is_ultra_restricted": false,
      "is_bot": false,
      "updated": 1493408178
    },
    "cache_ts": 1493408178,
    "event_ts": "1493408178.971282"
  },
  "type": "event_callback",
  "authed_users": [
    "xxxxxxxxxxx",
    "xxxxxxxxxxx"
  ],
  "event_id": "xxxxxxxxxxx",
  "event_time": 1493408178
}

When you send an event of type message the req.body looks like this:

{
  "token": "xxxxxxxxxxx",
  "team_id": "xxxxxxxxxxx",
  "api_app_id": "xxxxxxxxxxx",
  "event": {
    "type": "message",
    "user": "xxxxxxxxxxx",
    "text": "test",
    "ts": "1493407679.213224",
    "channel": "xxxxxxxxxxx",
    "event_ts": "1493407679.213224"
  },
  "type": "event_callback",
  "authed_users": [
    "xxxxxxxxxxx"
  ],
  "event_id": "xxxxxxxxxxx",
  "event_time": 1493407679
}

In parsing the event, slapp sends the meta data of user_id to be the user property of the body which is of different types in the examples above, one of type string and one of type object.
https://github.com/BeepBoopHQ/slapp/blob/master/src/receiver/middleware/parse-event.js#L29

Not sure how many other event types pass back user as the id vs as the user object but at the very least, one slack event does.

The expected outcome should be that the user_id is properly set to the actual id of the user.

[Feature Request] Support for loading dynamic select elements

I don't see anything in the documentation or code, but I was curious if there was anything in the pipeline to support dynamic options in dialogs (documentation). I imagine the implementation would be pretty similar to an action handler. If nobody is working on this I'd be happy to take a look at implementing it.

Creating a dialog

I see you have added the handler for a dialog, but how do you do the equivalent of slack.dialog.open?

Use .respond in a Promise resolution

When i try to use the .respond method in a promise resolution, it triggered an "infinite loop of slash command".
I need to add a .respond in the "direct scope"
Note: using babel-node to transpile sources

  slapp
    .command('/pr', '(.*) (.*)', (msg, txt, repo, team) => {
      slashHandler(repo, team).then((attachments) => {
        msg.respond({ text: '', attachments });
      });
      msg.respond();
    });

Support async/await

❤️ Slapp! Though I'd heart it more if I could use some ES7 bits, particularly async/await. Is there any way that you can think of to offer Babel with Slapp?

Can't consume a message.channel_join event if it's for your bot

Use case is I'm trying to drop a message into a channel after my bot joins that channel. The event for this looks as follows:

{
  token: 'xxxxxxxxx',
  team_id: 'TXXXXXXXX',
  api_app_id: 'AXXXXXXXX',
  event: {
    user: 'UXXXXXXXX',
    inviter: 'UXXXXXXXX',
    text: '<@UXXXXXXXX|slapp> has joined the channel',
    type: 'message',
    subtype: 'channel_join',
    ts: '1470699186.000028',
    channel: 'CXXXXXXXX',
    event_ts: '1470699186.000028'
  },
  type: 'event_callback',
  authed_users: ['UXXXXXXXX']
}

This is getting filtered out in the ignoreBotsMiddleware() function since the msg.meta.user_id is equal to msg.meta.bot_user_id.

One approach we could take is be a bit more specific about the message subtype we're ignoring. If we only want to ignore messages sent by the bot, we could also check where type=message and subtype=bot_message. I think you have a little more context on the nuances of why we need this middleware fn @mbrevoort, so curious on your thoughts.

default command text should match multiple lines

When using slapp.command() without a criteria, we default to matching all text, and then pass that in as the 2nd argument of the callback:

slapp.command('/beep', (msg, text) => {

})

Since we default that criteria to /.*/ it doesn't match multiple lines. We could change this to something like /[\s\S]*/m to ensure we match everything (including multiple lines).

SyntaxError: Unexpected token for starter-slapp-app

Used this script for user onboarding; basically whenever someone joins our default slack channel, the bot should send a direct message to the user separately. It's throwing some error right now..

slapp.match((msg) => {
  if (!msg.isMessage() || msg.body.event.subtype !== 'channel_join') {
    return false
  }
  isChannel('general', msg, (err, yes) => {
    if (err) return console.log('Error looking for general channel', err)
    if (yes) {
       
        slapp.message('direct_message',(msg)=>{
          slapp.client.im.open({token,user:${msg.meta.user_id}},(err,data){
            if(err){
              return console.log(err);
            }else {
              msg.say({ text: ` Welcome to slack` })
            }   
      })
    }
  })
  return true
})

Getting the following error..

/usr/src/app/server.js:117
slapp.client.im.open({token,user:${msg.meta.user_id}},(err,data){
^
SyntaxError: Unexpected token

You can view the live code being inserted in between 108 - 127 on https://github.com/abinashmohanty/user-onboarding-slapp-app/blob/master/server.js

Changing responseTimeout value

Cool project!

It would be great to have an ability to change responseTimeout value. As some hosted apps can have pretty high http latency and 500 ms can be not enough (3000 required by slack - 2500 timeout value defined in slapp)

Currently, we use the next trick in our handlers:

msg.attachResponse(msg._response, 500)

This helps us to avoid Darn – that didn't work. Feel free to give it another go. annoying error message from Slack.

Example code for add a smile reaction by the bot incorrect

// add a smile reaction by the bot for any message reacted to
slapp.event('reaction_added', (msg) => {
  let token = msg.meta.bot_token
  let id = msg.body.event.item.ts
  let channel = msg.body.event.item.channel
  slapp.client.reactions.add({token, 'smile', id, channel}, (err) => {
    if (err) console.log('Error adding reaction', err)
  })
})

The second parameter under reactions.add is wrong here as a string, maybe needs a 'name' attribute per slack api docs? https://api.slack.com/methods/reactions.add Thoughts?

Message.respond doesn't work for message actions

Greetings.

I've faced the issue related to message actions, looks like Message.respond doesn't do anything when I'm inside in message action handler.

Steps to reproduce:

  1. Register some message action.
// actions.js
const slapp = new Slapp(...);

slapp.messageAction(/my-message-action/, messageActionHandler)
  1. Trigger it.
  2. Inside that message action handler call msg.respond('Some text') and notice that actually nothing is happening (The message hasn't been posted in the channel).
// messageActionHandler.js

function messageActionHandler(msg) {
   return msg.respond('Some text from Message Action!');
}

Additional info:

  1. This bug occurs in the last version of Slapp. Currently, it's 3.2.0.
  2. Everything works just fine for common actions.
  3. If I comment out this if statement then Message.respond starts to work fine and I see necessary messages in the channel.

Any questions let me know.

chat.update API

Heya, awesome work guys!

Just wondering is chat.update supported, if not are there any plans for that?

Thanks in advance!

Opening a new discussion within a route() call.

Hello,

I'm experiencing an issue with a slapp.route handler. Here is the minimum code you might need to reproduce:

slapp.message('.relay <@([^>]+)>', ['direct_message'], (msg, text, userId) => {
  msg.route('hello', {userId: userId}, 10);
});

slapp.route('hello', (msg, state) => {
    msg.say({
      channel: state.userId,
      as_user: true,
      text: 'Hello you!',
    });
});

To explain a bit, I declare a handler in which a user can trigger some bot behavior : '.relay'.

This behavior somehow calls a route to execute some other behavior that is common to multiple messages handlers: 'hello'. The 'hello' handler is contacting another slack user.

The problem is that the message which should be sent from the route handler is not sent until the first user does not send a message (any message) to the bot.

Here is a screencast that demonstrates my issue: http://recordit.co/Mh4u7Wc7Qf

Thanks and keep up the good work !

Message.usersMentioned()

I'd expect the array to contain all the userIds of the users mentioned in a message towards the bot. So if a user sends a message like the following to the bot /bot text @username i'd would want to have an array like this as repsonse: [U#######]
Looking at the regex executed it's quite obvious that you parse the message.body.text for U####### occurrences. My Question is now: What am I getting wrong? And is there a shorthand to get a list of users mentioned (like @mention) in the message my bot receives via slapp?

Bot only responding to direct messages

Fully ❤️ Slapp, though I'm having one issue... My bot isn't responding to mentions or direct_mentions – only direct messages.

I've tried both:

.message('^.*', ['mention', 'direct_mention', 'direct_message'], (msg, text) => {

and

.message('^.*', (msg, text) => {

Even the example HELP_TEXT doesn't work:

.message('help', ['mention', 'direct_message'], (msg) => {

Any ideas?

Expose slack module directly

Since the slack module is stateless, would be convenient to expose it directly via a require:

// Either via a require
const slack = require('slapp/client')

// Or as a property
const slack = require('slapp').client

I think I'd prefer the first option of exposing it via a require, and just create a client.js that just module.exports = require('slack').

Can't get this to work outside of BeepBoop

Hi,

I am trying to make use of this lib outside of Beep Boop. I can't get it work and I'm not sure why not, I can see a few areas as the possible cause.

a) Context
I believe I have implemented the context function correctly so that it gets to my database and updates req.slapp.meta with the required information. At least if I look at req.slapp.meta before the function returns I see the right information. Though I had to declare the function async because I'm doing a database call so not sure if that is an issue.

b) ConvoStore
I am not too sure what this is about yet but I am using your MemoryStore for now so I assume that should work.

First issue is, the context function is hit several times and sometimes at what look random times with random data (well I'm sure it's not random but I can't figure the pattern). Most of the time when I type a message to my bot the context function is called twice. Looking at req.slapp I can see the same event twice, sometimes with the same ts, sometimes with different ts. It even occurred once that I received an event for a message typed minutes before (with other messages sent after that).

Second issue is, I can't get any of the slapp functions to work. None of the slapp functions are ever hit.

var slapp = Slapp({
    verify_token: 'xxxxxxxxxxx',
    convo_store: new MemoryStore(),
    context: async function (req, res, next) {
        // The meta sent by Slack
        var meta = req.slapp.meta

        // Connect to the database and get the info for the Slack Team who's talking to us
        await sql.connect(sqlConfig).then(pool => {
            return pool.request()
            .input('team_id', sql.VarChar, meta.team_id)
            .query('SELECT * FROM AccountSlackApp WHERE SlackTeamId = @team_id')
        }).then(result => {
            // Extend the meta with the required information as well as our own information
            req.slapp.meta = Object.assign(req.slapp.meta, {
                app_token: result.recordset[0].AccessToken,
                bot_token: result.recordset[0].BotAccessToken,
                bot_user_id: result.recordset[0].BotUserId,
                account_recid: result.recordset[0].AccountRecId
            })
            sql.close()
        }).catch(err => {
            console.log(err)
            sql.close()
        })

        console.log("> context <") // SEVERAL TIMES PER MSG?
        console.log(req.slapp)
    },
    log: true,
    colors: true
})

var app = slapp.attachToExpress(express())

slapp.use((msg, next) => { // NEVER HIT
    console.log(msg)
    next()
})

slapp.route('handleGoodDay', (msg) => { // NEVER HIT
    msg.say(':expressionless:')
})

slapp.message('hi', ['direct_message'], (msg) => { // NEVER HIT
    console.log(">>>>>> MESSAGE")
    msg.say('Are you having a good day?').route('handleGoodDay')
})

slapp.event('reation_added', (msg) => { // NEVER HIT
    console.log(msg)
})

I have no idea why. What could I look at in order to get more details and understand what's going on?

Thanks

EDIT - here is an example of going through the context function twice
twice_sametime

Error: unexpected server response (503)

Anyone getting such an error?
Error: unexpected server response (503)
at ClientRequest.response (D:\home\site\wwwroot\node_modules\ws\lib\WebSocket.js:720:15)
at ClientRequest.g (events.js:291:16)
at emitOne (events.js:96:13)
at ClientRequest.emit (events.js:188:7)
at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:472:21)
at HTTPParser.parserOnHeadersComplete (_http_common.js:99:23)
at TLSSocket.socketOnData (_http_client.js:361:20)
at emitOne (events.js:96:13)
at TLSSocket.emit (events.js:188:7)
at readableAddChunk (_stream_readable.js:176:18)

It happens from time to time and the server restarts :(

filter event by type and subtype

I think it would be convenient to be able to filter an event by type and subtype. This can be accomplished currently w/ custom match() or catch-all event(type) handlers, but having a way to match via the event() fn would keep things more terse.

I can think of 2 ways to tackle this:

  1. Overload first param to allow a subtype identified by a . in the string - depends on Slack not adding any .'s to their event types/subtypes
slapp.event('message.channel_join', (msg) => {
  //...
})
  1. Add an optional 2nd param of subtype, could be string or regex just like first param
slapp.event('message', /channel_join/, (msg) => {
  //...
})

Wrap calls to `postMessage()` w/ a simple queue

Currently if you chain a few msg.say() calls, you'll often get them out of order. I've gotten the following pretty frequently:

msg
  .say('Me too')
  .say(`Thanks for saying ${state.text}`)

image

You can work around this by nesting your says in callbacks, but it's kinda an ugly interface:

msg
  .say('Me too', () => msg.say(`Thanks for saying ${state.text}`))

We could solve this pretty easily by just pushing them into a simple queue that waits for the response from a call to postMessage() before sending the next one. A couple things to consider:

  • If bot is really chatty w/ lots of teams, it could cause delays in messages being sent (seems unlikely), we could make the queue unique to teams and/or channels
  • Could enhance this in the future to pause the queue if a rate limit error is received (would want queue per team here)

Less chatty logging

While it's nice to see all the messages coming in, I think a good default (or at least an option) would be to only log messages that are being matched, i.e. handled inside the slapp app. For example, if you setup a very specific message handler:

slapp.message('hi', (msg) => {
  msg.say('hi2u')
})

... then we would only log those slack events that came in and matched that specific handler, i.e. w/ the text of hi.

Invalid signature error when using singing secret

I've tried to use signing_secret and now I'm constantly getting Invalid signature error.

 const bot = slapp({
-  verify_token: verificationToken,
+  signing_secret: signingSecret,
   convo_store: conversation,
   context,
   log: false,
   ignoreSelf: true,
 })

I've tried to investigate it and found, that rawBody is undefined here.

And then I found that this function has not been called.
Probably bodyParser.json({ verify: verify }) doesn't work in any of src/receiver/middleware/parse-*.js files.

Node.js v10.5.0
body-parser v1.18.3
slapp v3.3.0

Easy tutorial to get started

Hi BeepBoop

I just tried getting this to work, and have never made a slackbot before, I must say there were a lot of googling and I think this bot framework is really specific to BeepBoop but you don't know that when you start out.

Also the first example look like it will work, but you don't know what to do afterwards? How do you use it?

Also it says it favors the event api over RTM, sounds good, but that kind of leaves you with creating a bot that is on https and is more difficult.

I should surely use something else because my requirements are much lower, but I did not know that looking at this project, it also looks easy.

😄

Add `defaultExpiration` to Slapp opts

The default of an hour is a good default, but in my case I don't want any expiration at all, and having to remember to pass , null, 99999999999999 to each route call feels wrong.

I suggest:

const app = Slapp({
  defaultExpiration: 60 * 60 * 1000 // 1 hour
})

Or to disable expiration:

const app = Slapp({
  defaultExpiration: 0 // or 'off'?
})

Register routers that don't need to be triggered by a user event

I would like the ability (or if this already exists, advice would be much appreciated) to set up routes on slapp that will allow me to pipe the result of one handler into another without having to wait for user feedback

Example use case:

module.exports = (slapp) => {
    slapp.action("some_callback_id", "some_value", firstPartOfPipeline)
        .route(secondPartOfPipeline);

    return slapp;
};

I realize that a potential workaround is to use the EventEmitter functions .on and call .trigger in firstPartOfPipeline function and do something similar to below, but in that case anything can trigger the "test" event and it makes it difficult to track the path of the message.

For example, the second action handler could trigger "test", and you would have no way of knowing by just looking at index.js. My goal is to have a sane method of tracing messages as it flows through my app instead of having to hunt down which module calls what.

module.exports = (slapp) => {
    slapp.action("some_callback_id", "some_value", firstPartOfPipeline)
        .on("test", secondPartOfPipeline);

    slapp.action("some_other_callback_id", "some_value", firstPartOfPipeline)

    return slapp;
};

username from message

Is there a way to get the name of the user who enters a message? I can get msg.body.event.user, which is the user id, but I want something meaningful as part of the bot's response

convoStore.set() w/o callback

Currently we call convoStore.set() w/o a callback in the message.route() fn. This works fine w/ our implementations of the store, since we default that callback to an empty fn, but is a bit awkward when writing a custom convo store (not obvious you would need to default that).

I suggest we pass a callback into that here: https://github.com/BeepBoopHQ/slapp/blob/master/src/message.js#L109

...and emit an error on this._slapp if there was a problem during the call.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.