Fun With Slack ChatOps
with Jason Lengstorf
Y'all asked for a real project, so today I’ll be working on some ChatOps ideas I need for my team at Netlify. We'll build out a Slack slash command and some integrations with Notion.
Resources & Links
Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.
JASON: Hello, everyone and welcome to another episode of Learn With Jason. Today on the show we are going to work on a real project because I put a poll out to see what people wanted me to do with an unstructured stream and very much in a surprise to me nobody picked Fortnite. I thought we were going to have a game stream and instead we are going to work on a real project. I am actually, I mean this is going to be fun because it is something I have been wanting to do for a long time. I find one of its biggest barriers to getting work done on a team is the context switching required to collaborate especially when you get into asynchronous work. If you are looking at -- you know what I did? I forgot to turn on all my lights. Let's set the scene properly. I can't believe I did this. Give me just one second. Now let's play my favorite game which is get my camera to recognize my face is a face. Just confuse it into looking... there we go. Hey, look, I am a person. I love the auto focus on this thing because it lets me move like this which I couldn't do when I had it on a fixed focus. But when it loses focus, I think my face is not a human face according to the auto focus thing. OK. Ambiance corrected. There is indeed a Melissa face on the shelf and I am not going to supply context because it is a surprise for later. Not later in this but later when the thing for that comes out. Let's talk about why ChatOps is the real project I chose to take on. I have limited dev time we can say. I think the real answer there is the only dev time I really get is if it is what I choose to do with my evening or this show, so why would I choose ChatOps of all the things that I could work on? Part of that is stemming from the fact that I am in leadership now so a lot of work I choose to do is around enabling and empowering the team and it is also just kind of fun to build bots. It is an opportunity to be playful and something people will interact with every day and maybe the most important reason which is I think it is really good for productivity. One of the things that is really hardworking async is you don't chance encounters. You don't have any of that sort of built-in safety net of a company where you can look around the room and see what somebody is working on, or if you have a quick question you can say where should I put this and somebody points at the board and you can go put it on that board and instead you have to have processes but process bloat is a really big problem. One of the biggest challenges I see is everyone wants a process but the process designed is cumbersome so no one follows it. Then you end up in this weird zone of we can't use process because it sucks and takes too long and burns too much of my time and you end up with these teams that function by, as far as I can tell just panic and sheer effort, and that's not a good way to operate. There are two pieces to this. One is I think a lot of time processes get over and you spend more time than you potentially should trying to put a million questions in the process and that's too many steps and people aren't going at a follow. The other piece is I don't think processes, like if I have to stop what I am doing and move to another app and make more choices and remember what I was working on before and come back to that app it can be challenging because that context switching is high even if it is kick like I need to capture an idea. -- quick. Finding ways to put this information in the right place without having to switch context is really important and I think that ChatOps is a really good way to do this because a lot of times what happens is you will be having a Slack conversation because we are all remote and somebody says that's a good idea and someone says capture that and you don't want to open Notion or JIRA or whatever and the notion stays in the back of the head and never happens or you write a Post-it note and it sticks to the computer for the next three years. I want to cut down on both the amount of process and friction of following through ChatOps. If you can run a command asking three questions and you go on that's less friction than going to Notion and fill out this eight-step form and you want things to be easier. That's the project I have been working on and put a little work into it in my evenings and the cracks in my day and I am going to use the rest of this stream to kind of work on that. But first, couple questions in the chat. Zander is asking what beans am I brewing and my favorite coffee shop in the entire universe is Cova Coffee. I love them. They are here in Portland. Whenever I go to the gym I reward myself by walking to the coffee shop after and did I choose my gym based on the coffee shop? Yes, I did. And I get the latte without milk because I am at the age where it doesn't sit with me anymore. I also brew at home. Zander you showed me that video about giving your beans a sprits to reduce mess and I think that absolutely changed my life. Web Dever, process has been burning me out. Yes, it can make it predictable and that can make cumbersome and burdening. As low friction as possible to follow the process meaning you can do things like the process is the way work gets done. It is a habit you build. The same way you open a pull request on GitHub to get work merge in. If you have a request for another team having a clear process to say this is the request and it is low friction to do you just build that happen and it starts to be something that happens. I have an idea and capture it like this. What I love is being able to do it like you would in Slack when you want to send animated GIF you do/-GIF and it works. What if it was slash-idea and it asks about the idea and you send it off trusting it goes to the right place. That's sort of what I want to build. That sort of thing. I have made some progress on it. Why don't we shift over into desktop view. Good. Good. And let me pull up here, before we start, we have got live captioning going. We are with Maggie today from White Coat Captioning doing all of the live captioning. That is on the home page of the site at learnwithjason.dev. You can find our sponsors on the page as well. That's made possible by Netlify, New Relic, NX and coming soon Plural Site. Thank you for making this show more accessible to more people. Very much appreciate it. We are going to be talking about Slack chat bots today and I am going to be hooking it into notion. Because I can't show Netlify stuff on stream unfortunately the way I was hacking on this is in my own personal Notion so we are going to find out -- let's see. Here is my test request. This is my request inbox here. This is what I want -- like when I am working on a project, what I really want is to know who is in charge of it, how big is -- I mean we are saying like how big is the risk if we don't do this but how do we know how to prioritize this? Is this a nice to have? Must have? Critical? It or like it doesn't matter and if we have time great and if not cool. The other is is it needed by a certain date. I think you have to attach a date. Date, I know everybody hates deadlines but here is what I found. No deadlines means no urgency means no one will work on it. If it is important, it needs to have some kind of deadline. Not like a we all get fired if we miss the deadline but in order to make this useful it should be out within two weeks, four weeks, six weeks. Then we can say looking at all of the things that need to be in October are we going do this or deprioritize this and you can start to make these calls based on how urgent it is and we need to know who submitted it and what the status is. The way I have built this board in Notion is we have an inbox. This is the stuff people requested and feeds triage and doesn't have a driver and then we have the project board. This is my personal account and this is my Netlify account. I am able to assign different drivers to things. On a full team it would be each person on the team. Once you get assigned as the driver for the thing it shows who did the thing and so we can do a little bit of -- let's breakdown how the Notion works.
First and foremost I have the it is needed by and most urgent comes up first and if it is overdue it drops to the bottom of the list which is awesome. No it is not. It is just in ascending order of dates. This isn't future. It is October now. That means that you kind of have ascending order of urgency and we have where the driver is empty. I could also mark it needs to have whether or not the status needs triage but what I kind of like about this is there are two things. One is choosing who is going to drive it and then there is the actual planning like the triage to get it ready to work on. I don't think both need to be true for a request to be processed. I think what we are looking for is is this idea something we are actually going to tackle and if the answer is yes, we choose who is going to tackle it. That's the in boxing processing. Then the driver can work on the rest of it, the triage and scoping and that gets it out of the triage stage but we should know who needs to triage it, right? That's my reasoning for only going by whether or not there is a driver and not necessarily worry about what status it is in. And the project board includes the understood to triage. This is the request board. So the way we would do this now is we would say somebody says I have an idea and we would say can you put that in Notion and the person has come in and create a new thing. And here is what we have to do. It is just how people are wired. They don't -- you don't fill out all the fields. You look at this and you go, you know, well, I am not 100% sure what we need to buy and we will figure out the risk later. Completion is not an urgent thing and it is not enforced in Notion so you end up with chaos. So for me this is the part that is really -- it is not quite there. It is not what I want. I want to improve this. The way that I am going to improve this is through a Slack command. Please open in a different window. It did. Then I have like a test Learn With Jason Slack instance. You can kind of see what's been happening here where I have got the bot, the bot shows what is open. This is the Notion stuff and we can see if we click on one of these it pops it open so we can see what is there and then we have also got the like a new request was created by the person who created it by so let's start by just I guess digging into that. Here is my Slack bot. It is up on GitHub. If anybody wants to check the code that I have already written, you can find that. I am Slacking off today, Chris, thank you for noticing. [Laughter]
Here is the code I have written so far and here is -- let's look at this flow and why I am excited about this. What I am doing here is I want to make a request. We are going to make a new request. It is a slash command and I am going to say do a thing. This is what I was going to write in Notion. Now when I send this it open ups this modal and I have to submit these things. I have to say I need it by next Friday. If you leave it default it is a nice to have. If I come up in here we need this. People notice if we don't have it. Optionally I can say additional details. I will say like we talked about this in the content meeting and thought it would be good to support the upcoming X-launch. Some kind of detail of why this is in there and all that good stuff and then I submit. When I submit it shows that a new DXE request was created by me. This is my user in this test Slack. It shows this is the thing, we need do it, it is needed by next Friday, here is the status and shows I created it because it pulls the Slack and Notion user and correlates those and includes these extra details in here. This is kind of what I am excited about because it creates this really interesting opportunity for like this is lower effort than opening Notion and finding the right database and all those things.
So that to me -- that's why I want this to exist. That's what I want to keep working on today. Let's open up the code. I have the repo here. Let's open it up. There are a few things that I want to exist. So let's walk through how it works first since otherwise this is not going to take a ton of sense. Here is my Slack bot. What the heck is N paths? I think I did this just to figure out why this is here. I don't know why I did that. Here is the actual command. So we are going to make this bigger so it is easier to see. And then we will hide the sidebar here.
What I wanted to do was create a -- it is a serverless function and the serverless function gets the body from Slack so this is like whether you run the DXE request, it gives a trigger ID and I am going to grab the Notion database so that I can get the data and then we go through and grab all of the options out of the database so this is pulled out of Slack. I will show you how this actually works. If I go into my notion database and I decide I am going to update my properties and we will say how big is the risk, we want to change this, we will add a new property and we will say -- let's see. Hide Jason will yell if you don't. We'll get that added and make it orange. Here is our cascading thing. Now when I go in here, and I go to create a new DXE request, it will now show that option. It synced what is going on here is synced to the notion database so it is pulling the right information and I don't have to edit the Slack command every time we need to make an update on this. So that then means that we get our options and then we want to go to the Slack API and open up a modal. The modal we are opening uses the Slack blocks API for modal so we have got a title, the callback ID is just something we can kind of -- if we needed to do special stuff we can identify it. If we have a bunch of these we would need to be able to identify which one it. We have the text for the submit button and submit Slack. The first is markdown on what to do and how to make a request. Let's open it up so we can look at the form as we go. This is the fill out the form. It has a link if you want to go straight to the Notion. I have it linked to Google because this isn't the live database. You can tag me if something is going on. Let me do the wrap here. This is the that Notion does link is ankle brackets pipe and the text and you can do the brackets around the username and it will expand to be a thing for the user. You can use the user ID which is probably smarter because if I change my username it would break this. This clearly isn't going to work if I have a different name. Then let's see. Reading the chat. Yes, you are right. Why did I do the thing I did? We have this section here and then we have got the title block which is, you know, what do you need with details. We have input and we can put example text in there. We have got our like longer, like the hint here. The hint is this bit here. We can see kind of what we are asking for especially because as soon as you type here your placeholder goes away. The placeholder you can type something like this. But then, oh, it replaces your hint. I don't like that. Oh, it comes back when you start typing. You get your hint there. That kind of says what you are being asked. When you need it by, risk and additional details. Here is the date block. You use a date picker. You have the label on it and when you need it by. All these IDs are arbitrary but you use them to select your information later. So how big is the risk if we don't do it and then we pass in these options and these are the ones that we pulled out of Slack up here or out of Notion up here. This bit? That's our options. And we just default to the first one which is low but if we actually change the order, I can change my default by going into Slack and like if I make this, the top, then that would be my default. By default we want it to be low because generally speaking if you aren't bothered enough to think about it is probably isn't too big of a problem. Multi line true makes it a text box instead of a text input. Then we are sending all of that through the Slack API here, right? I have just abstracted away the Slack API and the Notion API. If we go in and look at these, the Slack API is a fetch request to Slack API and then the end point is the one that we called back here. Open both of these so we can see what's going on. These are a little misleading at first because when you look at this 0 it doesn't like an end point. That's how Slack works. You pass in options and here we have the bare token and content type and you need to set a care set so the Slack validation doesn't say no care set. It clogs up your whole stuff. You also want to set a body. If the body is not undefined and the reason is you can do get requests for like a user at which point you wouldn't have a body. We default to get and if you pass in a body we will set the method to post and body to body and run the request and return it back and that gives us this response from Slack here. Because it is not something that needs a response it happens async we can just return nothing. Then you get a modal. That's how we are getting this modal on screen here. Then when you do something with that modal and you submit it we need to grab those details. That's what happens in here. We are doing another serverless function and we have the Notion stuff where we need get -- I wonder why I have all of these as separate things? Whatever.
We pull in the utilities from Notion to create a database entry and then we also need the Slack API because we have to respond. We first check to see if it has a payload, if it doesn't we can error out. Then we grab out that body and get the payload itself which is what comes from Slack. So the payload is the value of that modal and the other pieces there so the values are what actually comes out. That's like the -- this is where these names come in that I was showing you. The values are the responses to what the modals were in the view.state.values. Why set these block IDs here, description block and importance block, that is what we will use here. The importance and description block to get those values. That's why it is important to set it because otherwise they will auto gen one and it isn't human readable. It is annoying you have the block and the field inside the block but it is a choice that they made and that's the abstraction and it is good. It is fine. So this then gives us our actual data, title, date, description and importance. These are the four fields we need from somebody in order to do this work. We get our props. What we are going to feed to Notion is this type here. Can I get in here? We have the name, submitted by, needed by and how big is the risk to Netlify if we don't do this. I know this is a silly field name and this is actually something where wish I could do in Notion where you can have a label and ID but they use the label as the ID so when you have a human readable text for your database properties it is also the key which is silly but you know whatever. It is what it is.
This is going to be our request entry. We are going to build this out and we start with a title. For the Slack user, I want to get the user ID out of the Slack payload. Who is the user in Slack that submitted the modal. We then call the user's info end point and because this one is a get we pass in the arguments as a query string. That gives us back their email. Then we have a Notion util that the get the user by email and that gives us a Notion user and what I have decided for me at least is if there is a Notion user then we are going to set it. If there is not a Notion user that matches the Slack user they probably shouldn't be here so you could fail or send an error log or something. Send it to your security team whatever. I am not going to worry about that just because this isn't like a mission critical system. The only thing somebody could really do is post something into a database. There is no mission critical stuff here. And then we set the date. If the date is set which this always should be set but I figured maybe in the future I will make it optional so I did this check and same with the importance. If they add importance then we include it. Since I set it default this always be set. Better safe than sorry. If they add a description this is the piece that is children of the page. The way that Notion works is they look at these as -- let me get one of these open here. These are properties and this part down here is the children. These are child blocks of the page. Then once we have the children, so we just push in as a paragraph the description there, we send in the Notion API to the page's end point we will send in which database we are updating, the properties we set and the children. If something goes wrong, we are going to log it and otherwise we can then build out our request link so we know that Notion is the link. We is our database ID already. I want to get the response of the property and Notion does this really annoying thing where they add hyphens into the ID and don't use them in the URL and they are somehow are not interoperable and this is like cool, thanks, Notion. This is a regular expression that will find all the hyphens and replace them with nothing so you get a hyphen-les ID. The PMS is the query string they add for the sidebar peak. If you change that, you can -- how does this work? Open pages in sidebar center peak and when you open it pops like this. This is purely preference. You can do whatever you want. I don't care. I have liked the side peak so I have kept it there. You will see it adds the ID of the page and then it has got this PMS and that's for the side peak. If I take this off it will open in the center like that. That's how that works and then we have to build a Slack chat message. I want to send a message back to Slack that says somebody created a new issue. I have a channel ID and that's my testing channel. This testing channel is the one I have decided to post everything to. All error free. So, yeah, graham, in terms of tests, if we operationalize this and anybody other than my team starts using this we have an internal tools team and I will work with them to figure out what the right testing is. Our testing right now is none. This is what I am doing. I am solving my own problem in a way that is not going to be productionized. It will just be used on my team. And none of what we are doing is like mission critical. If our ability to put an idea if a Notion database goes down from Slack it doesn't matter. But there probably should be some test. I can do a post-message and we send to the channel ID and you can grab that ID. This is another thing that is annoying about Slack. They don't really teach you how to do this. If you don't know that good luck.
There is a Stack Overflow answer I found that shows how it works. That is going to then get one block which is a section of mark down that says a request, a new DXE request which gets the link you built here was created by the user ID so whoever it ran the slash command and that gives us this message here. That is the piece that gives the steady run of a feedback loop so I can be in here and right now I am talking to myself but if I was talking to somebody else I could say DXE request and an idea from our DMs. And I am going to run it. Happens right here. We will say I need it by tomorrow and no other details. I submit and then the DX team who would be in this channel gets a notification I just did that thing even though it happened over here. That's the other piece of this. There is an in-built Slack notification for which someone uses the slash command. I don't have one for wherever a new thing is built in Notion but I would love if everybody just used one flow and we will have to see over time to see if anybody is using it a different way and kind of address that. Yes, the VP testing strategy is I don't have time for this. If you care about tests, you run them. I am so sorry. Got to laugh so you don't cry.
There is another piece that I want which is a standard, like, running on a schedule. So you can see here that today at 5:00 AM, on Monday at 5:00 A M and last Thursday at 5:00 AM there is an automated run that grabs this list of untriaged issue. That's another important piece. We need to know if somebody submitted a request and we aren't responding we have to be accountable. If we want people to follow the process the process needs to be something we are accountable for. The call out and the date it is needed by so we can see holy crap we just missed a deadline and that's bad. That is run by this schedule function. I have a Netlify function. We are using the schedule which gives us cronjobs and then I have the Slack and Notion API pulled in. For my Notion query I want to run a query for the needed OK.
I know what I am doing. I am running to get the ID, the name and the needed by date from Notion and then the query itself, you go to databases and then your ID and query and that's the end point. So I am going to filter for anything that doesn't have a driver and sort by the date and then pass in that -- where is the actual issue? Database is part of query. Oh, this is the type. I defined the type. I am really bad at TypeScript and still figuring out how this works. We det it back as a notion query so I can use it and each issue is typed so I get auto complete. We do the same thing and build out a Notion request link. Probably could have made this into a util but didn't. We get the name and date out of the request. This is what is nice T. Gives you that auto complete. Then we return a Slack block so this is a section of mark down with the bulleted list kind of setup. I could set that up in a bunch of ways so it uses the bulleted list. It is not. It is just plain text. I kind of don't care. I am happy with this. This is fine. You could have a lot more fun with it and do all sorts of things. It is not important to me. Then if the issues are greater -- if the issues are zero I want to do nothing. I don't need to post like nothing to review. That's a waste of a notification. If it is 0 we do nothing. But if there is more than one unreviewed item in the database then we are going to do a quick check to see grammar because I couldn't handle the -- there are one requests. That always hurts my heart. Did this check. Then we go to post the message and we add in the message, spread the issue so we get those blocks in there and context that says here is a link to the full open request on Notion. And we just send that off.
It is a scheduled function which means it doesn’t have a return body. You just send the status code. And I have set this up, this was fun in cronjobs. These run on UTC, I think. I don't know what servers these run on. I am running it at the first let's go into prompt tab. It is minute, hour, day, month and day of the week. It is the first minute of the hour. The 12th hour of the day but because of the time zones I had to mess with it to get it into the right place. Day of the month I don't care what day of the month so it can any of the day month and any week but day of the week I do care about. This was new to me. I am saying on Monday and Thursday. Also this crontab guru is so dope if you have never used it. This is a handy tool for figuring out what this is. At midnight on Monday and -- or at noon on Monday and Thursday. If it is at 5:00 AM that's plus 7 -- it is GMT. GMT at noon and noon minus 7 puts it an pacific time which is 5:00 AM. This is a thing that does make sense logically and I understand it. Love it. We have a scheduled function.
There is nothing else that I have to do to this because I run the schedule and pass in the cron this function will now run every Monday and Tuesday at noon GMT. That gives me the ability to run this check automatically. I didn't have to build anything else. And then the only other thing that's probably worth calling out before we dive into more is that I built out some notion utilities. So the Notion client is an actual thing. The reason I needed this is because I wanted this collect paginated API because they restrict your results to a 100. I wasn't sure if over time this was going to end up being thousands of ideas or something. What I want is to make sure I get everything and didn't want to write it. So they have a utility that lets you query every item in a database even if the page size is a 100. It just goes to the next 100 until it runs out of results. We are getting all of the users in the workspace. We find the user that matches the email that we are checking for and then return that and we can connect a Slack user to a notion user. Then I have some types in here for all of the different notion blocks and properties. We have to send in a bear token. We have to include the notion specific things like telling it which version of the API you are using. You have to tell it you accept and are sending JSON because I think it can work with other formats as well. Get by default if you do send a body we make it post. Send that off to the right end point, the API.notion.com. For the properties, I wanted to have like how to get a title and instead of having to write this whole block of boilerplate around centering a title block I just want to put in the title and have it done. Same with the content. This is the broiler plate for making a property that is a rich text block, a date block, and a select. I just didn't care. This is for children the blocks are slightly different. You get paragraph and it will return one of these and the property is a rich text property. You are nesting this one inside of the block. As you can see this gets pretty cumbersome to just be writing this code. Which is why I ended up having the handler here. If you look at how I am sending in the data which is in the interactive one we build this out doing properties.select. I wrote them out to shortcuts myself. Get to the CSS already. I don't think I am going to write a line of CSS and you are welcome or I am sorry depending on what your motivations were for today. Here is what I want to do next. The next piece I want to figure out is I want a way for somebody to -- if someone floats in and says, hey, Jason, can you do this thing for us, we talked about it over lunch. You get these kind of tags where somebody will send you a message like this. The hard part is that like this is a lot of how async business happens. People just ping and say hey, do this thing. But there is no record of this. Once we get far enough in the chat log this message is lost. Somebody will rem remember sending it but it didn't get put in a backlog and there is no triage and tracking of the thing. So we need this message to be added to the thing. But it is really fun to have to kind of manage the emotional labor of being like hi, we have a database for this and we need you to put this request in this database and I have to go over here and find this database and I have to copy the thing and then I put it and that's the wrong thing and grab here and paste it in. Could you please move the request there and then I submit it. They respond back with I don't have time. Could you please do it for me and all these misspellings are there because they are responding on their phone. My team has to say we will break the process and do this or we have to take a stand and say I am sorry; we really need you to do this. Now it is a fight.
What I want is a robot that says this is how requests get done and you can choose to deal with it or not but it is not putting the emotional labor on the team to be the ones who are like the Slack cops. I think this is something I think about a lot. When there is something that's really challenging like pushing back on somebody because the people who are going to be the most guilty of this are like me. I am a VP. I am in the middle of a meeting and somebody has a good idea and I am like that's a good idea and throw it in Slack. Asking somebody who is not a VP to tell a VP they are not going to do the thing they asked for doesn't happen. So we need to robot to do it. Don't make the team do that. Power dynamics suck. But robots don't care about power dynamics. They will tell you F-off and that's what I want to build next. I want to build a new Slack command that will handle the F-offing. That's the piece we have to figure out how to do next. I am going to get into my -- how are we doing on time? We have a little bit of time. I am going to try to get into customizing Slack which is here. Manage apps maybe? I think that's the one. I want to manage my apps. I have built this bot and I am going to open it app directory. Configuration. Manage. Install apps. Custom integrations. No, I need -- there is a way to do this. I always screw it up. Add management settings. Build maybe? Here is my app. I want this one. Why didn't that just show up? Here is my actual app. I have my basic functionality and all these pieces. So the piece I care about the most, um, is that going to show a credential? OK. It doesn't show any that we care about. It gives you the app credentials and you have app level token and so on and so forth. [Laughter] And then if I was going to put this into the Slack app directory I could customize all of this stuff. But yeah. You know, maybe I want to have like an app icon or something. But so let's go into slash commands because that's what I care about.
I want to create a new one. We will call this use the process. The request URL is going to be wherever I put this. Let me show you one of the favorite things I learned about how to build Slack commands. I want to test the dev live but you need it to be a public URL for Slack to function. I am going to run Netlify dev live. This is going to open up so it pulls in all of my environment variables. That's fine. It gives me this temporary. Like this is an autogen. It gives me an URL and I can put this in here and go Netlify functions and we will call this one nudge. Remind someone to submit via notion you are going to run this as process nudge. I have to remember I call that nudge and save this. I am going to come back and create a function. That is called nudge.ts and I will go back in the command to remember what I am actually responsible for. Let's start with the stuff I definitely need. I have got my handler and I need to export handler and we will say -- does it need a body? Probably doesn't. I am going to do one of these. We will put this down here and then I want to -- do we need anything special? I don't think we do. I am just going to send a message. I am going to skip all of this. Head down here. We are going to drop this in. Now I think if I run this it won't fail. We should see it immediately. If I start typing nudge it says process nudge. Nothing happens. No error. That's expected. We didn't tell it to do anything. What I want to do next is send a message. I am going to come back down to the Slack Post-it message and put this in here. I need to actually send this. Let's go back here and grab Slack API out of my utils. Now I have got this. Not going to use the event so I can leave that out to avoid that whole deal. And this piece here would just be -- we talked about this. Submit ideas. Through the process. If that does what I think it does and we will see how well I have abstracted everything here then immediately upon saving this because I have the live tunnel open I can do a nudge. In the bot response. This is where it gets really powerful. Now when somebody comes in and they try to circumvent the process you are just like cool. Does this work in threads because this would be dope? No. We will have to figuring out how to make this work in replies/in threads. I hope that didn't show the whole thing. How does one run a command in a thread?
That seems like a thing that already exists. Incoming web hooks. Links. Slash command in thread. View slash commands. How about we go to Stack Overflow? That's always faster. Yeah. Not supported in threads./command cannot be used in messages. Boo! Can I get a shortcut? Are threads a Slack thing? Yes. What I am looking at here is you can reply to thread and that opens up the sidebar so what I would like to do is be able to put this reply in here so that it is kind of contained. But I wonder -- I can make this into a shortcut. Let's find out. I want a new shortcut. Global on messages. I will hit next. Callback ID. Let's go with... then we will say reminds people to submit using proper channels. And then callback ID will be like nudge. How about that? We create it. We go in here. They are all going to go here. So that means I need to -- I need to make a little utility here to send this part. Well, no I don't. I can go in here and do something like console.log of the event. And let's also update this one to be our live command for a minute while we are working. I have to remember to go fix that after the fact so it doesn't break on us. But for now because I have created this nudge when I come in here and I say let's do this and we will say -- here we go. Process nudge. I run that. It fails. But it does send us all of this data. This is what came back. It gives us the headers, payload and message action with tokens and the other piece that we need is if it gave us the type which I am sure it is in here somewhere. Callback ID. Nudge shortcut. We need to find this nudge shortcut and then we can do some stuff. Did I screw up the link? We got some subs and raids. Thank you, were the sub. What up? What do you all call yourselves? What's the crew? Like the -- draw the rest of the horse? Nicky, that cheesy old batman stuck spinner I had one those where the Learn With Jason spinner would spin but I got away from it because I worried it was too flashy and distracting. Plus I learned how to do this really cool after effect thing. I made that color wash thing actually. It makes me really happy. The first and only thing I have ever built in Adobe aftereffects. So then I need to use this payload. The payload is the piece I need so why don't we put this down here and use the payload instead of the event and it will give us more data. This is going to fail again but it lets us. I decoded it this time. Undefined reading state. OK. That's going to break. We have to check what callback we are getting before we do that piece. Let's log again. Because it is not a nodal. It doesn't have the state. Good. All right. It does give us a callback ID. We are going to say if payload callback ID equals shortcut. Then we actually want to do something different. So let's -- how do we want to do this? Let's do this. We will just return again. Reel just shortcut this whole thing. I probably want to abstract this out a bit so it doesn't turn into a nightmare code but to start let's just make it work. I am going to run the nudge. Come back here. Here is our response status 200. It gave us nothing. It shows us what the message is. That means we can do some shit. I am going to do this. I am going to grab the message user. Yeah, yeah. This is going to be great. I am going to get the user ID which is going to be the payload message user. Payload method user. Now we can make a message. It is going to be hey, @userID. We have talked about this. Then we are going to send that dang message. Let's get the nudge and I want to do this piece. We are going to change out this bit. Now if it works, it should tag the person who sent the message. That's closer. It didn't get in the thread so we have to figure out how to reply to a thread which I can figure out. We can do that. Boo. Now we have a response but it needs to be in the thread. Let's figure out how to -- let's see. Slack. Reply in thread. Need a thread of messages posted to a conversation. I don't care about that. Maybe we can figure out if there is a thread that owns. Token channel attachments. Thread. Time stamp. Post another messages time stamp value to make this message a reply. Avoid using a replies time stamp value. Use its parent instead. OK. Let's go back to the output here. Reply. This is the time stamp. Does that one give you a thread time stamp and then a time stamp? This is the one I want. We will start by looking for -- we will start by looking for a thread time stamp. We can say if payload message.thread and we can simplify this. Ts equals 4.load.message.ts. This one did not have a message. It only has a ts. That's all good. It does have a thread ts. You can just use the thread ts no matter what. We pass this in at attachment blocks text. I can just put in thread ts. That should this into a reply. Now I can nudge. Boom. Look at that. Someone that is definitely not me. I am off in Lalaland. You have to remind me. This one didn't have a ts. We do need that fallback to thread ts. Let's go back. And there is our reply. Dismount. OK. This is great. This is exactly what we need. Let's make this more useful. We can do something like -- the message should be like -- in order to make sure we don't lose track of any -- we ask that everyone use the -- use the Slack command. I don't think we need this part. I think we can just link. Is that right? We can link directly to here. Hint directly to our request notion. Here is another request. Tada. I want to deemphasize this. Let's use the Slack context thing which I have down here. That's on the cronjob. Here is a divider and a context. I can grab that and come back over here and we will say... up in here. We can put in our divider and take our message. Let's do that. We will do -- notion. Look, now we have got the actual message along with helpful details if you want to do more. This is pretty excellent. If you click on the button in the thread -- oh, it gets the thread. OK. This is cool. Some valid piece of information but this is not a request. Somebody else comes in and says oh, it would be cool if this was, you know -- if we added X to the site. This is the one that we want to nudge. So we come in here and we nudge and it would get this persons at. I wish I had two Slack users to show how this works. But that's great.
That's actually exactly what want. If we nudge here, it nudges the bot. That's awesome. This is really helpful stuff where we can do all those things and then if you try to run DXE request is it going to -- not supported in thread. It gives you a little feedback and you would come out here and run DXE request and you would get the form. That's a little bit of ChatOps. I think the next piece we want to do -- we have about 15 minutes left. Demme vitamin -- invite us to the Slack? No, I am not going to do that. But I can get this live. To get this live I am going to -- I am going to remove the slash command for nudge. It doesn't do what I want. Let's go to the slash commands. Yes, delete it. We have our shortcut. That our process nudge. Now we need to get this live. Let's save. You can delete this nudge then. Use it. Add a shortcut to nudge the process. We can do the push. And get that going live. And we will see that deploying. It shouldn't take long but I will get this live app. We can go in here and update this again. And once this finishes, we will be able to go and give it another test and make sure it does what I want. Linda, that's a great idea. You could use this slash command to like build out your 1:1 stuff. Like this is the idea, right? Whenever you have an idea you don't want to lose that idea. That's the whole point. But if every time you have an idea you have to stop what you are doing, move to a different app, capture the thing and remember where you were in the other thing that's not great. Let's do another idea. Find this nudge. And there it is. From the live site. It links through to the notion database. If you want to do that it is all good. This is now starting to feel like something we can really work with. What do you think, chat? How do you feel? What else would you use this for? Here is a question. I had never really thought about ChatOps all that much when I first started at Netlify. It wasn't something that was on my agenda or my mind. And then as I talked to other people like we had somebody who used to work at Netlify Gerald why he went over and worked at GitHub and I asked him what is different between working at Netlify and GitHub and he said ChatOps. He said there is so much automation running through our chat. That got me thinking because a lot of the most valuable things we do, we are all remote and almost all of us are in one chat program, and each of these is an avenue we spend most of your day in because that's where we get context, share ideas and ask for help. If we can make that a hub for a lot of the places we are working and a lot of the things we are learning and we can very easily get ideas out of Slack and into the more permanent places it should be then we lose that. Or we mitigate I should say some of the challenge of Slack is where everything happens but it is impossible to find things in Slack. If it is not a day old or newer it is just so hard to find. We had that conversation and that decision was made over here. Having a slash decision where you can say we have made this call. Slash decision at user at user agree that this is what we are going to do. That goes into a decision log. Now I want to go build that one. That would be huge. With that being said, we did a real project. We worked on some ChatOps. Got ourselves some working chat commands. If you all want to play with this, I actually did take the time to write down the tokens you need and how to get this thing up and running. I think I managed to get all of this in. Did I get all the -- the piece that I am missing I think is the permissions that are required. When you run this you will have to go add permission to your bot until it can do all things you need to do. But that's a good thing Slack does with the bots. Don't sleep on this live command because it lets you test web hook and chat commands and other things you have to deploy and test. This short circuits that loop because you can save it and run it again. That I think is probably the reason why I was able to get this done. Being able to test like this with the dot live URL but yeah. Get in here. Play with this. Make some stuff. Then I also have been playing with some discord commands because I have a Learn With Jason Discord. It isn't private but I don't invite anybody to it because I haven't figured out what I want it to be. But I started playing with ideas for discord and capturing guest ideas or capturing a thing we want to publish or a clip or something. Thoughts and lots of ideas. For what could be possible. Yeah. Dom does some really cool Discord stuff. We has been working with the Discord API. Demetrius Clark, where are you at? Everybody go follow Dom. Dom is good people. Also, does anyone know why the heck Twitter won't let me get out of mobile Twitter? Did they make the decision all of Twitter is mobile now? No idea what's going on there. I think we are going to call this episode a success. I am going to do one more quick round of shout-outs. We have had Maggie with us all day from White Coat Captioning doing the live captioning. That's on the site, learnwithjason.dev. That's made possible through the sponsors. Netlify, New Relic and NX all kicking in to make this show more accessible. Please considering following on Twitch. I know it makes a big difference to me to have those followers because that helps people discover the show on Twitch. If you want to subscribe on YouTube and like the podcast. Please do the like and subscribe dance. Check out the schedule. You can add the show on Google Calendar. Ryan Florence and team released React router 4 within the last few weeks and it does all sorts of cool stuff that is relevant to some of the big things we are exciting about remix are built into React router 6.4. Platform focused and lots of new stuff. Ryan is coming on to teach us all about it. We have an episode with Joyce and we will learn about Fido2 and how to log into a website with the fingerprint scanner or the face logo. Payload CMS and next.js. We are going to do component tests for web apps in cypress. Shandra is coming back on the show. We are going to learn -- I have to get this on the show. What have I done? This was great episode. We are doing another one. Make sure you -- like I said, like, subscribe, follow on Twitter and do all of the things because it makes me feel good and also helps other people find the show which is very useful. With that, y'all, we are going to call this a success? Who should we go and raid? I am seeing primagen. Anybody else y'all are seeing that you want to go raid. Live channels. Here is primagen. Finite singularity. Lana lux. Game dev. Music synced 3D action game. That's dope. Who else? Daniel heart? Linux home lab? Block monsters. Next JS. Jelly car. I think music sync 3D action is pretty dope. Is Jeslen live? Let's go raid Jeslen. All right. Let's do it. Why am I not following? Don't email me, though. Let's go raid Jeslin and we will see you all next time. Thanks as always for hanging out. This has been a blast.