In this chapter, we're going to implement Discord and Slack nodes. So far, we've learned how to implement trigger nodes, such as manual execution, Google Form event, and Stripe event. We've also learned how to create an execution transformation node like OpenAI, Anthropic and Gemini. But one type of node we haven't implemented yet is messaging node. So in this chapter I'm going to teach you how to implement two of them, Discord and Slack.
And this will give you enough knowledge to add any other messaging nodes that you prefer, like WhatsApp, Telegram, or anything similar because most of them work in exactly the same way. But before we do that, I do want to resolve some of the issues CodeRabbit mentioned in the previous pull request review. The most important one of those is credential ID injection. So what can currently happen? Let's say the attacker creates a workflow in our app and they go ahead and select their Gemini credential.
While this manual execution will at the moment really send the ID that it's written right here, This is just front-end validation. This can very easily be bypassed because anyone who is handy with the console can somehow inject any ID that they want to be sent to our network request and start a background job which would then use someone else's credential ID. So the only thing the attacker has to find out is someone's credential ID. For example, maybe they are making a video and they open their credentials right here, they click here and then you can see I have this ID up here and there we go. The attacker doesn't even have to know the API key because all we actually need is the credential ID.
So if they can somehow inject that right here, obviously this now doesn't make sense because this is my credential ID, but imagine I have another account and I just stole someone's credential ID, I could very easily spend their tokens. So let's go ahead and think of a way to fix that. The problem is basically right here. So I just opened the Gemini executor. And in this step where we fetch the credential, as you can see, the only thing we do is we just pass along the credential ID.
So this can be injected. Attacker can add anyone's credential ID here and then we would simply spend their API key. So let's do a quick fix of that. One that I think is the simplest and the easiest we can do is by revisiting our functions inside of the ingest folder. So ingest folder functions.ts.
Let's go ahead and let's do const user ID await step.run find user ID, asynchronous. Let's go ahead and fetch workflow like this. We can just repeat that. And the only thing we actually want here, so let's just do select user id true. That is the only thing we are interested in and let's return workflow.userid.
We are using find, unique or throw so this cannot be undefined. There we go. Now we have user ID which has to exist. And then what we're going to do is very simply anywhere just add user ID. You can add it anywhere because this is an object, right?
If we were just using plain params, then the order would matter. But in this case, the order does not matter. You can just add it wherever you want. So just pass in user ID here in the executor. And now let's go ahead and find out how these executor types can be updated.
So I'm going to command click instead of get executor, that will take me to features executions lib executor registry. And in here I find node executor and in here node executor params gives me what I need so I'm just going to extend it by adding user ID and making it a string there we go so now as you can see I no longer have any errors here and I can safely pass along user ID to every single executor. So now let's go back and let's revisit something here. So inside of source, Features, Executions, let's go inside of components, GeminiExecutor.ts, we can now go ahead and double check that this is correct by also passing the user ID from data and let me just check how do I now, oh yes, very simple. In the GeminiExecutor, not in data, just user ID as simple as that so now if this user if the attacker somehow manages to get a hold of someone's credential ID, they will have additional problem they need to resolve.
They also somehow need to spoof the user ID. So technically this isn't 100% protected still. You could still somehow inject the workflow ID, but at least it is no longer given to you on a plate to just enter any credential ID. At least we are making the job a little bit harder right now. So what I would suggest after you do this is just go ahead and try something.
Make sure you save this, maybe do a refresh of course make sure you have your app running here. I'm going to refresh and I'm just going to try and run this just to make sure it's still working that I didn't accidentally mess something up. So this is working and let's see that is working beautiful. So what you would do now is you would also go inside of OpenAI, Executor right here, and you can see that it's super simple now because every single one of these now has access to user ID. Some of them might not need it, but in this ones where we do need it, it is very useful.
User ID and just user ID. There we go. And yes, if you want to, you can also go ahead and publish errors like this. Whenever they happen, I think I forgot to do that in Gemini here. If credential is not found, Make sure you publish the error.
Just make sure you're using the proper channels. There we go. And instead of open AI, let me check if I accidentally I left Gemini node here. So open AI node. And then the same thing in Anthropic in the executor right here.
You just add user ID. And then in the credential, you pass along the user ID. And let's just go ahead and copy this right here and make sure you are emitting error for this stage. Great. I think that's exactly what I've outlined here.
Let's check. So we fixed missing channel events. I fixed one invalid node name in the logs for OpenAI and we fixed the credential ID injection. Great. Now let's go ahead and let's focus on creating some new nodes.
So the first thing I want you to do is go to my node based assets folder and in here you can find Discord and Slack. So just go ahead and add that to your public folder. I'm going to go ahead and go here inside of our public folder. We should have logos. So I'm just going to go ahead and copy Slack and Discord and paste it here.
So you should now have Slack and Discord. Great. I'm going to start with the Discord node. So I would suggest that before you do it, you at least create an account on Discord so you can test it out. It doesn't matter if you're not going to use it or not, it will kind of give you the idea of how all of this messaging platform works.
It's free, it's simple, it's fast. So just make sure you have an account for Discord so you can test this out properly. So I'm going to go ahead and copy one of the existing executions here. Let me copy Gemini and paste it here and let me rename this to Discord like this. And I just remembered of course we also have to update our Prisma schema.
So let's find our node types here. Let's add Discord and let's add Slack. Once we've added those, as always, npx prisma migrate dev and let's give it a name, Discord Slack nodes. Discord Slack node and that should synchronize the database. Great.
We can now close that and as always I recommend you restart your Next and your Ingest server. All right. Now that we have them ready, you can also refresh localhost 3000 every time you refresh your server. Let's go back inside of this one, Discord. We just copied it.
So I'm going to start with node.tsx. Make sure you are working in the Discord folder. This will no longer be Gemini node. This will now be Discord node. So we are just doing this whole rename thing that we keep doing.
Discord node, not data, just Discord node. Great. Now let's go ahead and start by changing the base execution know to use this court SVG and name this court. There we go. And I want to leave it like this for now simply so we can see it being added here.
So now we have to visit our node components, located inside of source, config. And then inside of here let's add node type, Discord, Discord node. You should be able to import it from Features, Executions, Components, Discord node. Then we have to go to our node selector located in Source Components, node selector. Let's go ahead and copy this, give this a node type of Discord, label of Discord, and let's go ahead and to send a message to Discord.
And make sure you're using Discord.svg. And now if you go ahead and click on the plus button you should find Discord. Here it is. Amazing. Obviously, when you double click on it, it will still use the Gemini configuration.
So let's go ahead and fix that. I'm going to close everything and I'm going to go back inside of Node right here. So let's start by changing the data right here. Instead of All of these we're going to have webhook URL, we're going to have content, and username. You can probably go with even less of these, but I think these are sufficient enough to make it fun and customizable.
Now let's go ahead and this is all good. For the description let's do the following. It will be nodeData.content If we have the content we're going to say send nodeData.content.slice otherwise not configured and yes obviously this is now throwing some errors because well we're not using the proper dialog at all So let's go inside of the Discord dialog.tsx and sure, let's start with the form schema. So variable name will still stay the same, credential is not required, none of these are actually required. So let's start with a simple one and an optional one username so username for the bot.
The bot can be named whatever you want. Then let's go ahead and let's add content. So content will be a string with a minimum and maximum length here. So I don't know where I found this online. Maybe it's true.
Maybe it's not. Maybe it's an API limitation. Maybe it's a bot limitation. But yeah, you can have a limit like this. And now we need a webhook URL.
So you can be as lenient as you want with this. For example you can just make this a string and make it webhook URL is required. And this is not how you do that. My apologies. So why use a string here?
Why not at least do dot URL? Well, remember, we have an option to use templating language. So if you mark this as a required URL, you will not be able to use any templates, right? Maybe you want to load the webhook URL from the previous node, right? So that's why we're using string and not Zod URL here.
Great. So we now have that and let's go ahead and fix these. So this will be username, this will be content and this will be webhook URL. Let's copy them. Let's add them here.
There we go. We can leave the watch variable name. Let's change this to be Discord configuration and let's go ahead and change the description to be configured to Discord webhook settings for this node. Now let's go ahead and change each field so variable name should be myDiscord. That's good.
Let's go ahead instead of the oh also we should probably change it here. Yes, my discord. Now in here, it's no longer gonna be credential ID. It will be webhook URL. So webhook URL, and this should be much simpler.
So we can remove select entirely, we don't need it. Instead of form item open form control like this, and just render a normal input. So very simple, and add a placeholder and spread field property. So if you want, you can give a placeholder just to give your user the idea of what they're supposed to add here. Then let's do form description.
And in here, you would basically explain to your user how to do this. So get this from this court channel settings integrations that hooks right. Whatever you prefer however you prefer to convey your user this information. Again I have no idea how to make this. I just copied it from somewhere.
Now let's go ahead and do the content one and this one will be a text area. So let's go ahead and demonstrate to our users how they can for example use variables here summary and then maybe a response right just to remind them they can do that my Gemini dot text. I don't know something like that. And in the form description again we're just you know explaining things the message to send use variables for simple values or JSON variable to stringify objects. And then let's go ahead and let's add, let's copy the input one here, because it's the most similar one, form field and let's replace the last one here.
And instead of webhook URL, this will be username, bot username, optional. There we go. Let's go ahead and add a placeholder here. Workflow bot. And then we can make the description be well descriptive.
So this will be used to override the webhooks default username. And of course, let's rename this from Gemini dialog to Discord dialog. And now we can remove the use credentials by type hook. We no longer need that, which means that we can remove image, we can remove select, we can remove credential type and use credentials by type. There we go.
Much simpler now. Now let's go back to the node and we can now import this court dialogue from dot slash dialogue and we can remove the Gemini dialogue. Looks like I still have this called Gemini. So I'm just going to go here, change this to be Discord form values. And let's go ahead and add them here And let's use them here.
There we go. And this is how it should look like now. Variable name, then webhook URL. Oh, this is still called system prompt. Whoops.
That should be called content. So inside of dialogue, let's just make sure to rename that. It's not optional. It is content. Let's be more descriptive, message content.
There we go. I think that makes it clear what you're supposed to write here. Basically the message that will be sent, right? Very, very good. Now that we have that, let's go ahead and create the channel so we can create the real-time execution thingy here.
So I'm gonna go inside of source, Ingest channels. I will copy Gemini, paste it here, rename it to Discord. I will rename this to Discord channel name, Discord execution, and this will be called Discord channel. Now that we have that we can go back, my apologies, we can go inside of ingest functions.ts and we can register the new Discord channel. So just make sure you import that.
Great. Now let's go ahead inside of features. Let's go inside of executions, components, discord, actions.ts. Let's go ahead and rename these to discord, so discord token, and fetch discord real-time token, and all instances to be Discord channel from channels, Discord. There we go.
Then we can go ahead and go back inside of node in the discord folder and we can fix this to be discord channel name and fetch discord real-time token and remove fetch gemini real-time token from actions and we can remove the channels Gemini import. Perfect. Now that we have that we can finally go inside of the executor right here. So let's go ahead and let's start with renaming the data here. Instead of Gemini data this will be discord data it will still have a variable name but alongside that it will have three new items webhook url content and username perfect And let's rename this to Discord Executor.
I like this. This time I don't think we will need user ID so we can remove it because it will be unused. Let's go ahead and start by changing the ingest channel to use this court one, this court channel. So replacing all instances to use this court channel, basically just replace every single instance to use Discord channel and maybe some of them are unneeded, unnecessary. We're going to fix them now.
So that's what I did. I just basically replaced eight instances of what was previously Gemini channel to discord channel. So we start with loading, we throw an error if variable name does not exist and then we can go ahead and instead of throwing errors if credential ID doesn't exist or if user prompt doesn't exist with something simpler. We're going to check if webhook URL is missing. So we throw an error like this and we're going to check if content is missing.
So message content is also required. Make sure to emit an error. And now let's go ahead and remove this because we're not going to need that. And let's do const raw content, handlebars, handlebars.compile, data content and pass in the context. Const content, let's go ahead.
And yeah, so the problem is the way handlebars will compile this will make it non-compatible with Discord. So I found that you can install a package called HTMLEntities and then you can decode it using that package and that should improve how messages arrive. So just import HTML entities. Let's go back here. So now that we have content, let's do decode raw content.
And let's go ahead and set the username. So let's check. Did the user pass this? If it did, let's decode handlebars result. Handlebars.compileDataUsernameContext.
Let me fix this. Otherwise, undefined. And it will just use the default from Discord. So basically, we are allowing user to use variables to set their system prompts. Oh, am I still calling this system prompt?
I think I just haven't refreshed. Oh no, it's actually the Gemini one, sorry. So let me add a Discord one. Here it is. Yes, so basically you can use variables here, here and even here.
So now let's go ahead and remove credential because we don't need it. We don't need any of these in fact. Let's just empty the try entirely actually emptied until published success like this and let's just do const result await step dot run this court webhook like this let's go ahead and do await ky make sure to import ky from ky so the same thing we used in our HTTP request executor if you remember ky so you should have it installed and now in the discord executor let's do await ky dot post data webhook url let's go ahead and pass JSON content content.slice 02000 because this is this Discord's max message length and the username if we pass it. Great. And then we're just going to go ahead and return context.
Whoops. Data variable name, this court message sent and set it to true. Or if you want to, you can just say message content and then pass in content.slice02000 because that's exactly what we are going to send. Whatever you think it's more useful to see. And then we can go ahead and publish this as success.
And we can just return the result because we are setting this here. Yeah, let's. Yeah, yeah, let's do it like that. Okay. And now I think what we have to do here is just change this check and move it from here and just do it here.
Basically inside of step.run because this is a new function scope. So TypeScript will not work from the check we did above. Same thing for this webhook URL here. If you don't want to use that, since we check it, let's go ahead and move it right here. There we go.
Perfect. So that should now actually be ready and let's get rid of this Gemini node, Discord node, variable name is missing, Discord node, webhook URL is required, Discord node, perfect. We can remove generated text and create Google Generative AI, we can remove Prisma. Great, so this is now ready to make a call to Discord's webhook URL and to send some message content using KY and to actually update the context with what happened. Now let's not forget to pass this to the executor registry.
So instead of executions, lib executor registry, let's add node type dot discord and add discord executor here. And we can copy and paste this and just add it for slack just so we get rid of the error here. But make sure you've imported the discord executor and feel free to use it twice for now. We're going to change it later. Great, so I think this should work just fine.
Yes, like one thing maybe we don't have to do is the return here like this. I think we can do it here I don't know It really does not matter if you want to do it. But if you want to do it here, you also have to do const result. Basically you can leave exactly as it was. I was thinking maybe it's simpler to move it outside of the scope.
Just make sure that you understand what is the scope of this function, right? So you can see this is where the function starts. Then we check if we don't have this. Then we do the POST call. We check if we don't have variable name and we throw the error.
And then we return this context data variable name. But this doesn't end the background job. We then publish the success and we return the result variable which is essentially just this. So this is on line 87 where this ends. So just make sure that you don't accidentally write this outside of the scope because it might return early or you might do something incorrectly here.
Great. So once we have this ready, we have added it to our executor. I think that's all we have to do. So Let's just call this my discord. Let's go ahead and do HTTPS code with Antonio.com.
Hello world. Node base but let's click Save. Let's go ahead and remove this. Let's go ahead and add this instead. Let's click save.
And let's watch this node fail obviously because it should not be able to make a POST request to that thing. So let's go ahead and wait for this. This is good. And then this obviously fails. And if we take a look at localhost 8288, we can see that it failed, fetch failed.
Yes, because this is an invalid URL. Great. So how do we create the proper URL? So let's try and find the exact way to do it. So get this from Discord channel settings, integrations, web hooks.
So in here I have a brand new Discord account and I have a brand new server so let me try and find this honestly I'm like learning this as I go just as you are so let me find this is user settings server settings, right click, maybe edit channel, integrations, webhooks. Here they are. Create webhook, new webhook. Oh, so okay, it already created one. So just choose this one.
You can name it whatever you want. For example, SpideyBot. Channel is general. Copy webhook URL and then we should just add that here. Let's click save.
Let's click save up there. Let me go ahead and open my general channel so I have no messages here. Let me execute the workflow again. And there we go! We successfully sent a message from our node-based project to here.
Amazing, amazing job. And you can see that it's actually super simple. It just needs a webhook URL. We didn't even have to install any Discord SDK or anything. It was super simple.
The hardest part was just repeating all the code that we already had and customizing it to be Discord related. And the integration for Slack is exactly the same. And I'm like 90% sure it's the same for WhatsApp, Telegram, Signal, whatever you might want to use. And it also kind of gives you the idea of how you would do it for something more advanced, like how to add it to Google Sheets. Well, very similarly, I suggest, right, you would just ping some webhook that they offer, or maybe if it's a bit more complicated, you would install some SDK.
And then the user will have to add like their API keys or something like that but nothing you haven't done before, right? That's why I try to choose not like every single node in the world but enough of them for it to be useful for you so that you can add your own nodes because honestly this video can go on forever. I can add a billion nodes here. So let's go ahead and now do the same for Slack. And of course we can also check in here in the completed tab how does the finalization look like and there we go.
So the variable is called my discord and message content was this. So just to confirm that that is also working as expected. Great. So let's go ahead and do the exact same thing for Slack. Shouldn't be too hard given that it's almost exactly the same code.
So inside of features, executions, components, let's go ahead and copy this code, paste it in components, rename this to slack. And then I'm going to go ahead and start with node.tsx and I will change these instances to slack and of course I will change the display name here. Then in the base execution node I will use slack image and slack title. And this will actually be exactly the same. So that's cool.
And now let's go ahead and do node components instead of source config. So source config folder node components. Let's go ahead and immediately add slack slack node. Make sure you import it. And then node selector node selector is inside of source components node selector.
So I'm going to go ahead and copy this. I'm doing this inside of my execution nodes. Change this to slack send a message to slack. Label slack and slack right here and I think that should be enough. We can now click the plus button.
If you scroll down you will find Slack right here. But obviously it uses a configuration for Discord. So let's go ahead and resolve that. Let's go inside of Discord. And let's go inside of dialogue.tsx and let's start by checking if anything needs to change in the form schema.
So this will actually be a little bit simpler because we're not going to have username and we are not going to have. Oh we're just not going to have username. OK. So we have content and in here I have no idea what is the maximum limit so you can remove it if you want it to be longer than 2000. This could form values.
Let's of course change these three instances to slack. So slack form values slack dialogue. And in here just remove username. We don't need it in here as well. And then you can also remove the input for the username here.
We don't need username. And I think that is it. We only need to obviously change how to do this. So there are a couple of ways that you can implement webhooks within Slack. You can do it by creating a new app or you can also do it by creating webhooks.
My apologies, not webhooks, workflows. I have no idea what is the difference between two, but I did find that if you want to use workflows, it is almost as simple as with Discord, which was just, you know, right clicking on the channel, edit channel integrations, add new. So I'm gonna go ahead and change this to get this from Slack, channel settings, and let's go ahead and do workflows, webhooks. Maybe this would be workspace settings actually. I think that's kind of what it would be here.
We can change the placeholder here just to tell the user like kind of what we would expect to have. And let's go ahead and change the title to be Slack Configuration and configure the Slack webhook settings for this node. Change this to be My Slack and in here to my slack. There we go. Now we can go back instead of node.tsx, we can change Discord dialog to be slack dialog and we can change Discord form values to be slack form values.
Now let's modify Discord node data to not have username because we don't need it. There we go. And now let's go ahead and close this. Let's double click here. Let's save this actually.
So I don't lose this slack node. Then let me refresh. And then let me double click here. And it still says, Oh, did I just change the entire thing here? Yes, again, I have modified this gorgeous dialogue instead of Slack dialogues.
Make sure the same thing is not happening to you here. So yes, Looks like I've been doing the entire modification inside of a wrong dialogue. My apologies for that. I hope you understood what I was supposed to be doing. So I'm just going to revert my changes.
My apologies. This is already a long chapter, so I'm getting confused. Instead of Discord dialogue, What I'm going to do is I'm just going to control Z and just return things here. There we go. Like nothing ever changed and bring back username here.
How simple is that? And then I'm going to go back inside of my node, which is my obviously my Discord node. And I'm just going to replace Slack dialogue back with Discord dialogue and Slack form values with Discord form values. My apologies for that. I think this is twice that it happened.
These components are so similar that I'm getting confused. Now I'm going to go back inside of source, features, executions components, Slack. And let's go inside of dialogue of Slack. Let's remove the username here. Let's remove the maximum here.
Let's add a comma here. Let's rename Discord form values, actually all instances of Discord to be Slack. Make sure you are doing it in the Slack folder. Don't make the same mistake that I did. You can remove username from here, from here.
You can change this to be My Slack. Change this to be Slack configuration, configure the Slack webhook, change the placeholder to be my Slack. You can go ahead and you can remove form field for the username. There we go. Now that we have that, let's also change how you get this.
So get this from Slack, workspace, settings, workflows, webhooks. Then let's go ahead inside of node of the slack folder. Let's go ahead and replace instance of this court dialogue with slack dialogue and this court form values slack form values. There we go. All right, So sorry about that.
And now you can see it says a slack configuration right here and it doesn't have the username field. So just to clarify. No you don't have to modify anything in the Discord folder anymore. We finished with it. It's working.
If you did, so sorry. Just bring back the username here. Rename this back to Discord. Make sure you're using username here. Make sure you're using it here.
This should be called my discord. These two instances as well. This as well. You also need the form field for the username. Here it is entirely.
If you've accidentally deleted it and the instructions here should be for Discord. Again, my apologies if you've changed the dialog in the Discord folder because you were following me. It was supposed to be changing the ones in the Slack folder. Excellent. Now let's go ahead and let's do the channel for Slack.
So I'm going to go ahead inside of ingest channels. I will copy this code and I will paste it here. I will rename it Slack. I will go inside of that slack. I will change this to be slack channel name, slack channel.
Let's call this slack execution. That's it. Then let's go ahead inside of ingest functions.ts slack channel and make sure you've imported it. Now that we have that, let's go ahead inside of source, features, executions, components, slack, actions. Replace all instances of discord with slack, replace three instances of discord channel with slack channel, change the import to slack, there we go, Then in the slack folder go inside of node.tsx, change this to be slack channel name, fetch slack real-time token and remove fetch Discord real-time token and remove Discord channel name import.
There we go. Now that we have that, let's go inside of executor for the Slack component. So First things first, let's quickly modify the Discord data and remove the username. We're not going to need it. Then let's replace all seven instances of Discord channel with Slack channel.
So just replace all of them and fix this to use the slack import and then you should just be using slack channel everywhere. Let's rename this from Discord Data to Slack Data. Let's change this from Discord Executor to slack executor. And now let's go ahead and change all instances of this court node text with slack note. And I think that's all of it.
Yes. Make sure you're not logging this court note anywhere. Now let's go ahead and let's remove username since we are not going to need it. This will be slack that hook And let's go ahead and do this. So KY dot post data webhook URL and in the Jason let's just do text and send that as content.
So yes this one will accept text but you will see. So I'm not going to add a comment. The key depends on workflow config. So you should probably add this instruction somewhere in the dialogue for the user. You're going to see what I'm talking about.
For now, make this text. But this can actually be anything. It will depend on how user sets up their webhook. So I think this is okay now. Now let's go inside of executions, lib executor registry, node type, oh we already have slack, perfect slack executor.
There we go. So now that we have that working, let's go ahead and remove this one. Let's go ahead and connect this, my Slack, HTTPS, let's do code with Antonio again, hello world Slack, click save, save up there. And let's just see this fail because that's what it's supposed to do, right? We have added an invalid webhook URL.
Looks like this is not working, but it could be because I'm missing a refresh. Let's just try again because this one just failed which is correct. So let's see. Yeah it just needed a refresh. Perfect.
And it failed for the exact same reason. It cannot post that. So now let's go ahead and let's test Slack. So just as with Discord, I've created a new account here, new channel, new everything, and I've created a brand new channel called General here. So as I said, there are multiple ways you can send a message to Slack using webhooks.
But one that I found actually the simplest is by again, you'll have to excuse me. I am not that familiar with Slack here. In More, you can find Tools, Create and find Workflows and Apps. And instead of Workflows, you can click on New, Build Workflow. And for an event here, you should be able to select from a webhook starts from a third party event.
So if you ever become a big shot with this app, you could probably collab with Slack and then have node base here. Obviously, that's why all of these other companies have it so much easier to add integrations because they have diplomacy between these apps to make it easier. But in our case we have to click from a web hook and this is what I was talking about. You have to set up variables here. So for example you can set this to be content and then inside of your executor this would be let me just find it.
This would be content right. If you set the key to be text this would be text right. So that's kind of the tricky part. So let me just see in this word executor. What's it called.
If it is its content. Let's go ahead and make it content here. Content and leave this key. So the key depends on workflow config. This is what I'm talking about.
So make sure the key here is content like that and data type should be text. Click done and there we go. You can see now example HTTP body exactly what we are doing and let's go ahead and click continue. And Let me go ahead and try and do add steps, send a message to channel, search all channels, select your channel and in here you can insert the variable here. Oh, you can also insert all data or you can just do content.
Okay, I see. You can see you can play around with this. So let's just add content. Save. There we go.
Finish up. The button up there. NodeBase workflow. Like this. And let's click publish.
And now somewhere here we should find let's click add to channel. Okay, node based workflow right click. We have copy workflow link let me check is that what I think it is. Let's see if I go here and if I add that here. I'm not too sure that's what it is.
As I said, I'm not like too familiar with this. I discovered this myself. So let me try and find copy workflow share workflow. Maybe it is this. I'm not even sure myself anymore.
Workflows here. It is copy workflow link. Starts with a web hook. Oh, here it is. You have to go and find starts with a webhook.
Click on the edit button and down here find web request URL. Okay. And add it here. And click Save. Click Save up there.
And let's see if this will not work. So execute workflow home general and let's see if it will work. There we go. Hello world from Slack. Both nodes successfully ran.
Amazing, amazing job. And in here, here we have my Slack message content, Hello World from Slack. So I've just told you, I've just shown you two different messaging platforms that you can use and integrate. And this, you can now use this as a guide on how you would add a billion others. Now WhatsApp, Telegram, Signal, whatever you prefer, 90% of them will work the exact same way, right?
Just some kind of webhook. Again this is kind of a tricky part with Slack 1 so I would suggest that you go instead of your dialog for Slack and maybe somehow add like multiple form descriptions. Make sure the key is content. So your users then know how they're supposed to Yeah, how they are supposed to configure the workflow because it's not exactly perfect. Because if inside of this webhook, they don't set up the content variable, it will not work.
Variable, it will not work. So make sure you have content variable. I think that would be kind of... I'm not sure. Another way you can do it is by exploring Slack apps.
So that's this. And then You would have to create your app and then you can send a webhook event as well. I personally find this just a little bit easier because apps have this weird interface that I find confusing to use. This isn't perfect either, but it's kind of fast to do. It wasn't too difficult.
Amazing. So that's it. Again, so sorry if I misled you with the editing of the Discord folder and caused you some problems there. I meant to edit the Slack folder. They are so similar I don't even know which one I'm modifying anymore.
So let's go ahead and check what we were supposed to do here. We added Discord node, DialogExecutorChannel and we tested it and we did the exact same thing for the Slack node. Amazing. Let's go ahead and push this to GitHub. So 26 Discord and Slack nodes.
I'm going to go ahead and create a new branch. 26 Discord Slack Nodes. Then I'm going to go ahead and commit all of my changes here. So stage all changes. 26 Discord Slack Nodes.
Commit and let's go ahead and publish this branch. Once this branch has been published, let's go ahead and open a pull request and let's have a Code Rabbit review for any security issues so you can see what you could improve or if we did something critically wrong so that we can fix it in the next chapter. So I've actually realized that 90% of this code was copied from the Gemini node and Gemini node is something that we have reviewed in the previous chapter. So I'm not sure how much sense it makes to, you know, let the code rabbit review the exact same code twice. We already know the potential caveats it's going to give us.
For example, most of the time we're not using entire try catch methods and things like that. Basically, what we already saw. So what I suggest is we read the summary and we merge this and then we use CodeRabbit Review for the next chapter, which will be a completely new feature instead of this one, which is pretty much identical to the previous one, just some slight differences with naming and executor. So we added Discord webhook execution node to automate message sending to Discord channels. We added Slack webhook execution node to automate message sending to Slack channels.
We added HTML entities dependency. This is so it improves the formatting of the message when it arrives into Slack or Discord. And we updated the database schema to support new execution type nodes. Amazing. So now let's go ahead and let's just merge this pull request.
And once it has been merged. We can go ahead and go back inside of our main branch. We can click on synchronize changes. And let's go ahead and double check right here with our graph 26 discord slack nodes that we have merged it. Amazing.
I believe that marks the end of this chapter. Not much things left to do so we are nearing the end of this tutorial finally. Thank you so much for being so far along with me here. Not too many things left to do. Amazing job and see you in the next chapter.