'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