How to add a super simple webserver to your discord bot.
Ever thought about receiving HTTP requests with your discord bot but you weren’t sure how do go about it? I faced this same issue a few days ago and figured I’d give it a try with FastAPI!
You can chose any web framework, but it has to be async
. There are a lot of alternatives to FastAPI: Quart, Sanic, Starlette and so on. You can probably find more in the awesome-asyncio list.
(If you unsure what async
means you should probably visit a tutorial like this before continuing).
FastAPI
Let’s start by looking at a basic example for FastAPI
Install fastapi
and uvicorn
with pip install fastapi uvicorn
uvicorn is the ASGI server we’ll use together with FastAPI.
Then create a python file. You can call it whatever you want but I’ll call it bot.py
.
That’s it. Now simply run it with uvicorn bot:app
and you should be able to visit http://127.0.0.1:8000/ in your browser and see {"hello": "world"}
.
discord.py bot
Next step, a basic discord.py bot!
Install discord.py
with pip install discord.py
You will need to create an application on discord.com/developers and add a bot to it, from there copy the token and replace it in bot.run("token")
. You can find more details on this in the “Creating a Bot Account” page of the discord.py docs.
Once you replaced your token you can run your bot with python bot.py
Two in one?
Now on to the exciting part. How do we combine these two examples 🤔
That was the easy part, but how do we run either of the two now?
Let’s say we add bot.run("token")
at the end and run the bot with uvicorn bot:app
…
This event loop is already running… From the traceback we see that bot.run
tries to loop.run_forever()
. Which then yells at us that the event loop is already running since.. it was started by uvicorn. So what now?
Looking at the discord.py docs we see that bot.run
initialises our event loop by roughly doing something like this:
Since our loop is already running we can just use bot.start
directly I suppose?
Let’s try adding the following to our code from earlier:
Okay… but how do we run this in our code?
The easiest way to run async functions (or rather coroutines) from sync context is by creating a task. This can be done with asyncio.create_task(run())
(you will need to import asyncio
at the top, it’s a standard library).
Let’s recap what we have so far
That’s it! 🎉 Both FastAPI and discord.py should now be running at the same time. You can access http://127.0.0.1:8000/ and also run !welcome @maku
Additionally…
One amazing feature of uvicorn
while developing is the --reload
flag which gives you hot-reloading whenever a file change is detected.
If you wish to use a specific port you can do so through --port 8080
To access FastAPI from another computer you will have to host it on 0.0.0.0 instead of 127.0.0.1, this can be done through --host 0.0.0.0
My typical run command for uvicorn is: uvicorn bot:app --reload --port 8080 --host 0.0.0.0
A feature I love from FastAPI is the automatic generation of documentation. If you access http://127.0.0.1:8000/docs you will find an easy to use, interactive OpenAPI page
There are tons more, so go and check out the FastAPI documentation! ⚡