Reputation: 902
I am looking for a way to integrate a REST API inside my Discord.py
, using the rewrite branch. I want to use aiohttp
to handle requests but I am unsure of which approach I should take. The objective is to make for example a GET request to the API that would return a list of guilds the bot is in. Or as another example, a POST request that would ask the bot to write a given message into a specific channel. Overall it's about giving instruction to the bot from a webpage.
I tried putting the aiohttp app router and runner inside my Discord.py client class. The web server is indeed running, I made an async function to return guilds the bot is in, but it looks like the function won't take the request argument I pass to it when going to http://127.0.0.1/guilds
. Thus leading to a missing 1 required positional argument
error.
import discord
import asyncio
from aiohttp import web
class MyClient(discord.Client):
async def on_ready(self):
print('Logged on as {0}!'.format(self.user))
async def get_guilds(self, request):
response_obj = self.guilds
return web.json_response(response_obj,status=200,content_type='application/json')
app = web.Application()
app.router.add_get('/guilds', get_guilds)
web.run_app(app, port=80)
client = MyClient()
client.run(TOKEN)
Besides, the aiohttp server does not run asynchronously. I expect the on_ready(self)
to run, but it never does. What am I doing wrong ?
Upvotes: 5
Views: 5258
Reputation: 902
Well I found a way.
from asyncio import gather, get_event_loop
from logging import basicConfig, INFO
from discord.ext.commands import Bot
from aiohttp.web import AppRunner, Application, TCPSite
from sys import argv
from api import routes
basicConfig(level=INFO)
async def run_bot():
app = Application()
app.add_routes(routes)
runner = AppRunner(app)
await runner.setup()
site = TCPSite(runner, '0.0.0.0', 8080)
await site.start()
bot = Bot(command_prefix="$")
app['bot'] = bot
try:
await bot.start(TOKEN)
except:
bot.close(),
raise
finally:
await runner.cleanup()
if __name__ == '__main__':
loop = get_event_loop()
loop.run_until_complete(run_bot())
routes = RouteTableDef()
@routes.get('/guilds')
async def get_guilds(request):
client = request.app['bot']
guilds = []
for guild in client.guilds:
guilds.append(guild.id)
response = Handler.success(guilds)
return json_response(response, status=200, content_type='application/json')
Upvotes: 3
Reputation: 60984
I think the below should work to run the two tasks together. This isn't great design though, and will only get more complicated. You may be better served by separating your bot and your server and using a more structured way of communicating between them than sharing memory.
import discord
import asyncio
from aiohttp import web
client = discord.Client()
async def on_ready():
print('Logged on as {0}!'.format(client.user))
async def get_guilds(request):
response_obj = client.guilds
return web.json_response(response_obj,status=200,content_type='application/json')
try:
bot_task = client.loop.create_task(client.start("token"))
app = web.Application()
app.router.add_get('/guilds', get_guilds)
web.run_app(app, port=80)
except:
client.loop.run_until_complete(client.logout())
Upvotes: 2