Host a Python Discord Bot on AWS Lambda (Free and Easy)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Welcome to this video where I'm going to show you how to create a Discord bot in Python and host it on AWS Lambda. Once you've added it to a server, you can say "hello" to it or you can get it to echo back a message that you've typed. By hosting it on AWS Lambda, it's going to be available 24/7, but you only pay for it when you use it. AWS Lambda is also really cheap and it has a generous free tier, so you can serve thousands and thousands of requests per month completely for free. This is perfect if you want to learn how to create a Discord bot that you can use for games, for music, or to even build an app or business on top of it. Let's start with a quick overview of what we are going to build. We're going to use a Python Flask app as the bot itself. So this is where we will write all of our custom logic for the bot. We will host this app on an AWS Lambda function and it's going to have a public API endpoint. When someone uses a command on Discord, like this hello command, Discord will send that to our API and our Python app can do something with it and send back a response. Also, we have to let Discord know what commands our bot will support. To do this, we can call Discord's API to register this "hello" command. That way, when somebody tries to talk to our bot on their server, they'll see a list of popups showing which commands are available for that bot and how you can use them. And we only have to do this whenever we create or update a command. Now, doing it this way is a little different from a traditional Discord bot. Normally, you can use a Discord Python library to create a bot and then just host it on a server like EC2. But if we host it serverlessly on something like AWS Lambda, it has a lot of advantages too. For example, it's easier to maintain. You never have to update or patch the server. It's a lot cheaper or free most of the time because you only get billed when it's being used. And it's available 24/7. It can scale up or down really easily no matter how many users use your bot at the same time. And you also don't have to worry about things like your server going down or restarting. Now, I'm going to try to make this tutorial as simple as possible, but I recommend knowing some of these things in advance before starting. So that's Python, AWS, and Docker. But I'll try to explain it so even if you don't know them, you can follow along. If you're ready to get started, then go to this GitHub repository to get the code. The link will also be in the description below. The first thing you're going to want to do is setting up a Discord bot on your Discord developer account. To create a Discord bot, first log into your Discord account in the web browser and then go to the Discord developer portal. So that's discord.com/developers/applications. And then click up here to create a new application. So just fill in the name with whatever you want and then hit create. Once you have this app, go to the left-hand side panel and click on bot. And here you can configure the bot's avatar and the bot's username. Scroll down to this "privileged gateway intents" and then enable this "message content intent". We'll need this if we want the bot to be able to read messages in the server. Here I've updated the image for my bot and my application. I've called it "AWS LambdaBot" and I've changed it here as well. And now if you want to add the bot to your server, go to "OAuth2", click on "URL generator" and then click on "bot". And then down here you can either select a bunch of permissions you want your bot to have or if you just want to really go quickly, just select "administrator" and that will give it everything. And then copy this URL and paste it into a new window. And now you should get this prompt asking if you want to add this bot to your server. So you can select your server here and then click continue to add it. Once the bot is added to your server, you should see it show up in your members list. And you can create a channel to talk to it directly or you can just direct message the bot as well. But right now we don't have anything set up on the backend, so it's not going to do anything, but at least the bot's there in your server. Next, we're going to want to register the Discord commands that your bot will support. So this is basically telling Discord what commands it recognizes and what those commands take as input and what they do. I'm going to start by creating a YAML file with all my commands in there. So here I've got two commands, one that says hello and one that echoes a message back to whoever sends it. And this one is going to have a parameter. So here I can name the parameter and this one is going to be called message. Now it's also a little fiddly. There's a bunch of things like this that you need to know beforehand and I found it very hard to get the documentation on. So I actually just asked ChatGPT to write me this YAML and I think it worked. And if you want to add new commands, just copy paste one of the ones here and then just feel free to edit the name. Once you have the YAML file, we're going to need a Python script to read the contents of this file and then send that as a message to our Discord bot to say that these are the commands we want to register. So here I have another Python file. I've called it "register_commands.py". And let's go through this file really quickly. First, we're going to need two libraries. We're going to need the "request" library and YAML. So if you don't know how to install that, just type "pip install requests" and "pip install pyyaml". It's in the requirements file here. And the first thing we're going to do is set up our token and application ID. So to do that, go to your developer portal and then click on the bot. And then for the token, click on this reset token button. And you're only going to get to see it once. So copy it and keep it somewhere safe because if you forget what it is, you're going to have to reset it again and the old token will be invalid. So click that and you'll get a copy of your token. And put that token in here. Next, we're going to need the application ID. So go back to your Discord developer portal, click to the general information for your app, and then you should find your application ID right here. So just copy that and then paste it in our app. And here's the URL endpoint that we need to call to register the command. This line here will read the yaml file that we looked at earlier with all the commands. And it's going to use this yaml library to turn it into a Python object. We're then going to create a header that has this bot authorization token. And then we're basically just going to send a request for each of the commands to this URL saying that we want to register this command. Once you've updated the script and you're ready to register the commands, go to this folder in your terminal, the commands folder, and then just write "python register_commands.py". Then run that script and then you should see a bunch of successful responses. Go back to your Discord channel. You might need to refresh or reload the app. But once you do, if you type "/" in a channel where your bot is, you should now see that the commands you registered have shown up. And for the commands that have a parameter, like this echo command here, you should also see that parameter show up. And when you click that, it should also prompt you to provide this input. Now you can try sending this command, but it's not going to work yet because we actually haven't implemented the backend server for the bot. So let's do that next. Next, we need to create an HTTP endpoint for the bot. So if you remember from this diagram here, the user interacts with our bot by typing "/hello" into Discord. And then Discord knows that this is a command because we've registered it. And now it can send that command over to our bot using this HTTP endpoint. The code for that is actually pretty straightforward. So here I've got a new Python file called main.py, and I've just imported Flask. Again, if you don't have this library, type "pip install flask" into your Python environment. We're going to create a Flask app and we're going to use this index route with a post method to create an interaction endpoint. So here's going to be our interaction function, and we can also print out the request that comes in so that if you want to debug it or if you want to see what the request looks like, you can go to the Lambda console later on when we're running this and then look at that object. But now I'm going to create another function to actually deal with the request just because this is Flask's way of creating an endpoint and I don't want to load this particular function with too many details. So I've created this other interact function where I'm going to pass this JSON request. And here's the interact function, which will deal with the raw JSON that's coming in. You might have to go to the Discord documentation if you want a full breakdown of what this request could look like, but here are the basics. So the request is going to have a type, and if the type is equal to one, this is a Discord's health check, which you kind of need to support if you want Discord to validate that your bot works. So here it might send periodic health checks, and what we need to do is just reply with a type one as well. So this is just like a ping to the server. Otherwise, if the request type isn't one, then we can look at the other details of the request and see what it does. So here we're going to get the data from the request, so any parameters that the user puts in will show up here. And here's the name of the command. So if they typed a "slash" command, that will correspond to this. Here I've got two commands implemented. So I've got the "hello" command and I've got the "echo" command. I haven't implemented the "goodbye" command in this, the one that we've registered here, so feel free to go ahead and try to implement that on your own if you want to have a little exercise. Otherwise, this is how we check which command comes in, and then here, because this echo command has a message parameter, this is how we can get the message data as the value. So here I'm going to change what content I echo back to the user depending on which command's coming in, and then I can respond with this JSON object. We also have to use JSONify on the response data so that Discord can interpret it correctly. Finally, if you want to be able to test it locally, you might also want to add a line like this so that you can run this main . py app and have it become a local server. I have instructions in the README on how you can do that. So you basically just start up the Flask app using Python, and then you can send a cURL request with this "test_request.json". And I've kind of included that in the folder, so if you go here, you'll find this test_request. json, and it's just a sample of what an input request from Discord might look like when someone tries to talk to your bot. This next step is actually something I've wasted a lot of time on, and it's the token verification step. It turns out that for Discord to validate your endpoint as an actual bot, you need to add some security to it. By default, if you just publish this endpoint as a public endpoint, then anybody can talk to it and anybody can interact with your bot. But that's not very secure. You could be exposed to hackers, or people could just attack your endpoint and take valuable data or make it do stuff that you don't want it to do. So to prevent that, Discord will have a special token that it will send along with its request. And it's our job to validate that the token is legitimate and that it's actually from Discord itself. And since no one else will have access to the secret token, that's how we know that it's actually a legitimate request. Adding it isn't so straightforward. You can go to the documentation and read examples on how to do it here. There's actually an example here in Python, but where this is complicated is that we have to figure out how to add it to our endpoint, but we also have to figure out how to package all of these dependencies and host it on AWS Lambda. Dependencies that are to do with cryptography are usually harder to package because they rely on a lot of system libraries. So this is why we're going to use Docker later on to make sure that when we add these dependencies, they have the right libraries. If I build my Python app on a Mac and then I try to host it on AWS Lambda, which is using a Linux runtime, it's going to have compatibility issues. But first, let's just focus on adding this verification to our app. First, you're going to want to "pip install" this Discord interactions library. Go back to the Flask app and import it. So here we'll import this "verify_key_decorator" function. Next, we're going to need a copy of our Discord public key. Go back to the Discord developer portal and scroll down to your application where there's this public key field over here. So you could just copy that. You can just paste it here directly as a hard-coded thing, or like me, you can just get it from your environment variable and then we'll pass it in later. And finally, we'll use this verify key decorator with the public key, and now this interaction endpoint will be protected. So that's really easy. With just these three lines, we've now protected the endpoint and we're ready to publish this to AWS Lambda so that we can link it to our Discord bot. As I mentioned before, we are using quite a few dependencies with this application. So to make that work smoothly on AWS Lambda, I'm going to use a Docker image. So I'm going to put this Python application inside a Docker image. That way, I know that all the dependencies will have the correct versions, the correct architecture and runtime, no matter what machine I build it on, and that it's going to work seamlessly on AWS Lambda. Back in the project, I'm going to modify my directory structure a little bit. My main.py is now going to be inside this app folder, and this app folder is now going to be inside this src folder. So that's going to be the source code. And in the src folder, I'm going to have a Dockerfile and a requirements.txt file. So the idea is that when I deploy to AWS, it's going to take this whole app folder and copy it into this runtime. And the Dockerfile is really simple. We start by using the base image for an AWS Lambda Python function, and we're going to use Python 3.11. Then we're going to copy this requirements.txt, which is all the dependencies we need. Don't worry if you don't know what some of them are yet. You should know Flask and Discord interactions, but I'll explain these two later. So get those into your requirements.txt, and then our Dockerfile will copy that and then "pip install" into that image. And finally, we're going to copy our application logic, our actual bot, into this Lambda task root as well. So if you don't know, this Lambda task root is basically just the folder where Lambda starts its run. So when it starts executing this function, that is the home folder. So we want everything in there. And here we use this command parameter to tell Docker—or I guess to tell Lambda—where to start when it runs. So here the "main" is the name of this file, so make sure the name matches up. And "handler" is going to be the name of the function that we run. We don't actually have that yet, so we're going to create that next. When we run this main.py file as a Lambda function, it doesn't really have an endpoint like when we run it as a server or when we run it locally in our terminal. Instead, Lambda just sends this big blob of data to our function, and it needs a handler, which is the function that will receive this data and do something with it. So we have to do a bunch of stuff to kind of convert that request into something that Flask will understand. So to solve that, we're going to use these two packages. We're going to use mangum and asgiref. So "pip install" those. Again, they're in the requirements text here as well, so make sure you have them. And then you can import them into your file and just put these lines underneath your Flask app. These two things will convert this Flask app into a handler function that Lambda can send a request to, but then Flask can understand that information. It transforms it, and it turns it into something that Flask will understand. So now our interaction will work. So that's all you have to do, just these two lines, and we will be ready to use this on AWS Lambda. So now we have a Discord bot written as a Python Flask app, and we have a Docker image to contain all of that. The next step is to deploy this to AWS Lambda so that we can get that interaction endpoint URL for Discord to use. To do this, I'm going to use AWS CDK. If you haven't used it before, then take a moment, pause the video, and check out the documentation, just because I think it's a little bit out of scope for me to explain everything about how it works. But in the basic level, it's infrastructure as code. It lets us create AWS infrastructure, but write it as code, like this, for example. So yes, we can go into the AWS console and just click and upload our image. You could do that too, or we could use the CLI to do it. But I prefer it this way just because it's a lot easier to work with, and it's also really easy to update and keep track of. We can also basically commit our whole infrastructure to the same repository. Now, all the stuff I've been doing is already inside a CDK project, but if it's not for you, then you can either download this project from my GitHub, and then you'll get the whole CDK project structure out of the box, or you can just create a new CDK project. And normally, that's just done by using this command, "cdk init app" and then "language typescript. " But you will have to install CDK first. Now, in the CDK project itself, open your lib folder, and you should have your stack for the bot. It's just going to be three simple components for the stack. So first, we're going to have the Lambda function itself. This is really simple. We just use this "DockerImageCode" construct, and then we use this image asset, which is going to be our source folder, the one with the Dockerfile. So when we write this here, this whole thing is going to transform this Dockerfile into an image, upload it to AWS, and then associate it with this Lambda function. Also, if you want more details on how to set this up, I have another video covering Docker on AWS Lambda specifically, so check that one out first if you want to know more. I've set a timeout for 10 seconds because by default, I think it's only two seconds, and that might be too slow for a Docker container, but you can always increase this if you need more. And for the architecture, well, I'm using a Mac M1, which is an ARM architecture. But I think most of you, if you're using a Windows or a Linux, might actually be on this x86. So just make sure you check what your architecture is and then set it to this one, because that's the only thing you need to be in sync with AWS Lambda. Your machine architecture and the Lambda function should have the same architecture. And here, this is where I will actually hard code my Discord public key. So go to your developer portal, get your public key, and then just put it here. And then when the Python app runs, it will read it from the environment variable. And now that we have a Lambda function, I'm also going to create a function URL out of it, because remember, Discord needs an HTTP endpoint. So basically, this will just give us a URL that we can just click, and it will activate this function. And I've just set a bunch of stuff here. Again, if you don't know what this is, feel free to read up on it, but it's not really that important. Now, this last construct doesn't really do anything significant. It just makes it easier for me to see and access that function URL when I run the deploy command or when I go to my AWS console. So this just stores the actual function URL name there. It just makes it a little bit easier to find that endpoint. Now, once all of that is ready, go back to your terminal in your project root and then run "cdk bootstrap" if you haven't already. And then just change the region to whatever you're using or leave it blank if you don't know. And then when you finish bootstrapping, you can deploy it. Once it's deployed, you will get this URL here. So go ahead and copy that. And you can even visit it if you want to in the browser, but it's going to fail because we've put a lot of security on it and I don't think we even allow GET requests. Doesn't matter though. Just go back to your developer portal, go to your application, and then scroll down to this interactions endpoint URL and then paste that endpoint into here and then click Save. And if your endpoint is set up correctly, the save will be successful. If it doesn't work, then double check the steps again or go to your Lambda function and check for errors. You can do that here and just go into monitoring and then basically just check the logs. So click into these and see if there's any errors that show up or if your keys are not put in properly or something like that because if you fail to save it on this page, it usually means Discord tried to verify the bot, but it failed for some reason. But if you are successful and the changes are saved, now the bot should be ready. Once the function is deployed, you can go back to your server and try interacting with your bot again. Sometimes the first interaction can take a while because if Lambda hasn't been used in a while, it kind of goes to sleep, so it needs to start up, and that can take a few seconds. But once you use it, the next interaction will be faster. Let's try our echo command as well that takes in the message parameter. Okay, and I think it's working pretty well. Congratulations, you've now learned how to set up a Discord bot and deploy it to AWS Lambda. You can interact with it in your server by typing slash commands and it's pretty much going to be available 24/7, but it's also going to be really cheap. If you enjoyed the tutorial and you want to see more stuff like this, please subscribe to the channel and then let me know what kind of content you want to see. Otherwise, I hope you found this useful, and thank you for watching.
Info
Channel: pixegami
Views: 7,624
Rating: undefined out of 5
Keywords: python, aws, discord, discord bot, python aws lambda, discord lambda function, host discord bot aws, host discord free, deploy discord bot, create discord bot, flask tutorial, python flask, code examples, available hosting, serverless, cloud hosting, bot development, aws cdk, discord api, scalable hosting, aws lambda tutorial, docker image, python discord bot, discord hosting tutorial, discord bot tutorial python, discord hosting aws, host discord bot 24/7 free
Id: BmtMr6Nmz9k
Channel Id: undefined
Length: 21min 39sec (1299 seconds)
Published: Mon Sep 04 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.