Ethan Field
Ethan Field

Reputation: 4740

Why can I not send a message via Discord.py from a function?

I have created a script which takes in a message formatted as !notice [MM/DD/YY HH:mm], message, target which then calls a function using threading.Timer to call it at the time given in the message in UTC.

Where I am having trouble is sending a message from this function, I can't seem to get a message to send from the function regardless of the input of the message.

See below:

import discord
import asyncio
from datetime import *
import threading

client = discord.Client()

@client.event
async def on_message(message):
    if message.content[:7].lower() == "!notice".lower():
        try:
            notice = [datetime.strptime(message.content[message.content.find("[")+1:message.content.find("]")], "%m/%d/%y %H:%M"), message.content.split(", ")[1], message.content.split(", ")[2]]
            await client.send_message(message.channel, 'Created notice "'+notice[1]+'" to be sent to '+notice[2]+' at '+str(notice[0])+' UTC.')
            threading.Timer((notice[0] - datetime.utcnow()).total_seconds(), lambda a=notice[1], b=notice[2]: func(a, b)).start()
            print(str((notice[0] - datetime.utcnow()).total_seconds())+" seconds until message is sent")
        except (ValueError, IndexError):
            await client.send_message(message.channel, 'Incorrect Notice Format.\nMust be "!notice [MM/DD/YY HH:mm], Notice contents, Target".\nEG: "!notice [01/01/2017 12:00], This is a notice, Siren Raid Team".')

def func(message, target):
    print("Func called")
    for i in client.servers:
        for c in i.channels:
            client.send_message(c, target+message)

client.run(MY_SESSION_KEY)

This returns "Func called" so I know the function is being called, but no exceptions are raised and no message is posted in my chat.

I also tried substituting func with:

async def func(message, target):
    print("Func called")
    for i in client.servers:
        for c in i.channels:
            await client.send_message(c, target+message)

However this throws up an exception:

RuntimeWarning: coroutine 'func' was never awaited

Frankly, I'm out of my depth here. Is there any reason why this won't work?

I saw online that asyncio is not thread-safe. But, unless I'm misunderstanding, my first example didn't use that library in the function. Could is still be causing problems?

Upvotes: 2

Views: 3697

Answers (2)

Alex Tsankov
Alex Tsankov

Reputation: 41

In case anyone needs help with just sending messages to a server from inside of a flask function. I spent hours with this problem myself after going down the rabbit hole of asyncio and threading. Turned out to be way easier than I thought. Webhooks, unlike a regular Discord bot, are entirely synchronous, which means they can run in a flask function no problem.

If your goal is to just simply send a message to a channel from a flask endpoint and not use any other functionality, try webhooks.

from discord import Webhook, RequestsWebhookAdapter
webhook = Webhook.partial(WEB_HOOK_ID, 'WEB_HOOK_TOKEN', adapter=RequestsWebhookAdapter())

and then post the message

 webhook.send('MESSAGE', username='WEBHOOK_BOT')

Creating Webhook tutorial

Discord.py Webhook Info

Upvotes: 2

Tom W.
Tom W.

Reputation: 123

discord.py's discord.Client.send_message is a coroutine and must be awaited, like you did in your second code snippet. However, threading.Timer does not support coroutines. What you're looking for is create_task, which enables you to run a coroutine on the event loop. Since most of what your coroutine does is sleeping (mimicking threading.Timer), your on_message will proceed to run, given that you use asyncio.sleep and not time.sleep - the latter blocks the event loop. Here's an example, including passing arguments to functions:

import asyncio

loop = asyncio.get_event_loop()

async def sleep_and_add(a, b):
    await asyncio.sleep(3)
    print(a, '+', b, 'is', a + b)

async def on_message():
    # prepare arguments to your function
    loop.create_task(sleep_and_add(2, 3))
    # continue doing other things

Upvotes: 3

Related Questions