Building a Slack Chat Bot

Handling User Input

Jason Lengstorf

Jason Lengstorf

Learn With Jason
Building a Slack Chat Bot

Check out a free preview of the full Building a Slack Chat Bot course

The "Handling User Input" Lesson is part of the full, Building a Slack Chat Bot course featured in this preview video. Here's what you'd learn in this lesson:

Jason updates the application endpoint to handle the user input and return a message to Slack. The returned message displays in the same channel and includes a mention of the user along with their submitted message.


Transcript from the "Handling User Input" Lesson

>> So this modal is what we designed it to be, right? So we're able to say kind of we define start a food fight. We've got a little description of what it is and then what we need people to do, including a hint here. Because once I start typing here, this goes away.

So you can add some details there and then it gives you more info about like what is required. You can mark fields as optional if you want, but in our case none of these are optional. So at the moment, if I try to submit this, I am gonna say something like peanut butter is a controversial thing.

So we'll say peanut butter and jalapenos Got to make sure you get the diacritics right, are delicious. And it's a jalapeno, so it's spicy. So I submit this, and it's going to fail, because we don't have any way of handling this modal yet, right? So it says, hey, there was some trouble connecting.

That's because it tried to send something and there was nothing to handle it. There's nothing registered for it to send this to. So it just times out. It kind of flies into the void and nothing comes back. So it's like, I don't know, maybe you didn't build this yet.

So, the next thing we need to do is to actually build this. We're gonna add interactivity here. That means we're headed back to the browser and we're gonna full screen this, and let's head into interactivity and shortcuts here on the left-hand side, And we wanna turn this on.

So, the way that interactivity works is there are a lot of things that slack can do that it counts as interactivity. If you click a button, if you respond to a modal, if you run a contact shortcut or a bunch of other things, it is going to call that interactivity and it sends it to one URL, which is actually kinda nice in comparison to the commands.

Because the commands you have to add a URL for each one, and that's useful for separation of complex stuff. But in our case, it's kinda nice that it all just goes to one place because we've got the switch commands to handle it. So we're just gonna use the exact same URL here, actually.

So I can let's see. I have this somewhere in here. Grab this, drop it in here with the /api/slack. And we can save. Okay, what we're doing now is we're sending it we can see these requests coming in as I submit. Probably a little easier to see if I make this bigger.

That we've got all these happening here. So submit, there's another one, there's another one. And so we're sending these responses, but because we're not actually handling the interactivity, nothing is actually happening. So to do that, we are going to head back into the function here and we're gonna create a new function underneath our handleSlashCommand.

And this one's gonna be called handleInteractivity. And this one also takes a payload and that's gonna be the slack modal payload. And this one, we're gonna grab out the callback id because we need that. And that's gonna be the payload, callback id or the payload.view, callback id.

And the reason that we're checking both is that depending on whether or not we're sending a shortcut or a modal, it changes a little bit. So this is just a quick fallback. That means we're not gonna have to remember to change this later when we add additional pieces.

So let's set up a switch here. We're gonna do this just like we did in the handle slash command, where we're switching on the callback id. And in the case of the one we just created, our food fight modal, we need to do some stuff. So the stuff we wanna do is let's get our data and that's gonna be the payload view state values.

These objects are large. Then to get our actual fields, we wanna define the data that we need. So it's gonna be our opinion, and we'll come back and actually set these our spice level. And the submitter. So I wanna know who sent this opinion in so that if we have questions we wanna follow up we can do that.

So to get these we are gonna use this data. So data.opinion_ block. Remember I said that we were using the id _ block? Then we get to .opinion and then the value. So, this pattern just makes things a little bit easier. There's no way to get around the block really.

And I don't know if they need to be unique. So I just, or they do need to be unique because otherwise they would, you'd end up with kind of a mess. So this is the simplest way I found to do it. You can come up with whatever naming convention you like, if you would like.

So, then we've got the spice level block, the spice level itself, what the selected option is, and then the value of that. And finally, for the submitter, that's not part of the form, that's part of the original payload. So, we can get the user and their name. And what you could do if you were if your team is all under like a single domain name, and you all sign into Slack with your work email, and you all sign into Notion or GitHub or whatever it is with your work emails, you can map these where you can do a lookup for the user in the service that you're searching for.

We're not gonna do that today because it's this is not like a work instance. And so, if somebody else joined and they're using a Gmail account, we're not going to find their their email, because there's no guarantee that they signed up for Slack with the same email that they signed up for Notion with, right?

But it is an option. It's what I did on a previous team because everybody needed their at And that allowed me to even have people get updates in Notion for something they'd created in Slack. It was a really nice workflow. But now that we've got this, we will eventually save these, but for now we are going to just send the details back to Slack.

So we're going to use chat postMessage. We're gonna use a channel. And the channel that we are going to use is our general channel. So in this case I don't want to use the channel where it was submitted. I wanna use this channel's detail directly. So I'm gonna copy this channel id, and then I'm gonna go back into here and post, oops, in quotes, paste that here.

So that gives us our channel id. So anytime somebody starts a food fight, it will post to the general channel. The way that I did this in previous teams is if somebody made a request for help with my team, it would post in my team's public channel and tag the person who wrote it so that they would know where we triaged issues.

Next, we're gonna set up some text and we'll say, dang y'all, And then we can put some emojis in there because why not? And then we'll say we're gonna use a specialty thing that that Slack does where you can send like a tag. They do it like this.

You put it in curly boy or angle brackets like this. And then you can add somebody by either their username or id and that'll turn into an actual mention in the message and then we can say just started a food fight with a will do fields spice level.

Take and then I want this to have some new lines. So we'll just do some new line fields there, and then I'm going to say fields.opinion, and why don't we make that bold? And then we can add a couple more new lines and just encourage everybody to discuss.

We're gonna break under that. So, that is our basic setup here. And then if we want to have a default case, we can just console log. We'll say no handler defined for payload. We'll actually, we'll just send in the callback id. And then we can return a status of status code 400 and it'll be a body of this as well.

So that it shows up oops, so that this shows up on the screen for the person who posted it because again if something's wrong like that you do want it to show up. And then finally, down below our switch case, we're gonna return a status code of 200, just like we did in the other one with an empty body so that Slack knows that we did, in fact, respond to this request.

Then to use it, we are going to come down in here. And we're gonna check to see if there's a body payload. And if there is, we're gonna parse that. And this is sent as JSON, so we'll do a JSON.parse and get a body.payload. And then return handleInteractivity and pass in that payload.

So let's give this a try. Let's see how it's doing. So I'm gonna run my foodfight command. And then I wanna also show, because we're checking for text, we can add the take right in here. So let's just say, Raisins are a garbage snack. So that'll get picked up and automatically filled into that input, which is a nice way to do it.

It lets people kind of move at the same speed that they would normally type. They just have to prefix with that command. And I don't know that this is a particularly controversial thing to say. I think this is probably 50-50 in the population. So we'll submit it. And there we go.

It's now responding to the whole channel. It tags my user and says that I just started a food fight. It includes the take that I put in, and it includes the spice level that I set for it. So, now we've got kind of a discussion happening inside of slack.

And this could be good enough for some cases where if you just need a way you to, if somebody takes a particular action, have kind of an audit log of what actions were taken and when.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now