'Python + Flask + Discord: How to send a message through discord through a flask endpoint?
I'm trying to send a message with discord, activated through a Flask endpoint
I get the following error message when I call http://127.0.0.1:5000/send
RuntimeError: There is no current event loop in thread 'Thread-4'.
I have the following (minimal) code
import discord
from flask import Flask, jsonify
async def my_background_task():
for message in ['a', 'b']:
await client.wait_until_ready()
channel = client.get_channel(CHANNEL_ID)
await channel.send(message)
await client.close()
def sendMessages():
client = discord.Client()
client.loop.create_task(my_background_task())
client.run('SECRET')
app = Flask(__name__)
@app.route('/send')
def send():
sendMessages()
Solution 1:[1]
Maybe you should consider using webhooks instead of a bot. Here is a simple example you should implement flask to it.
import requests #dependency
url = "<your url>" #webhook url, from here: https://i.imgur.com/aT3AThK.png
data = {}
#for all params, see https://discordapp.com/developers/docs/resources/webhook#execute-webhook
data["content"] = "message content"
data["username"] = "custom username"
#leave this out if you dont want an embed
data["embeds"] = []
embed = {}
#for all params, see https://discordapp.com/developers/docs/resources/channel#embed-object
embed["description"] = "text in embed"
embed["title"] = "embed title"
data["embeds"].append(embed)
result = requests.post(url, json=data, headers={"Content-Type": "application/json"})
try:
result.raise_for_status()
except requests.exceptions.HTTPError as err:
print(err)
else:
print("Payload delivered successfully, code {}.".format(result.status_code))
#result: https://i.imgur.com/DRqXQzA.png
Solution 2:[2]
Sometimes using webhooks cannot provide the required functionality.
If it is possible for you to switch from flask to quart, you can use the direct support for async methods to gain controll over the event loop as you intented to do in your given example.
The following snippet starts the discord bot within the same event loop as the quart server.
@app.before_serving
async def before_serving():
loop = asyncio.get_event_loop()
await client.login(TOKEN) # this could be done outside of this method
# do not use client.run
loop.create_task(client.connect())
A complete minimal working example would look like follows
import discord
import asyncio
from quart import Quart
app = Quart(__name__)
client = discord.Client()
@app.before_serving
async def before_serving():
loop = asyncio.get_event_loop()
await client.login(TOKEN)
loop.create_task(client.connect())
@app.route("/send", methods=["GET"])
async def send_message():
# wait_until_ready and check for valid connection is missing here
channel = client.get_channel(CH_ID)
await channel.send('XYZ')
return 'OK', 200
app.run()
The connect of the client could be triggered by the /send
method itself, however it is important to not create a new task at every request.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Abdulaziz |
Solution 2 | C. Mayer |