Rapptz / discord.py

An API wrapper for Discord written in Python.
http://discordpy.rtfd.org/en/latest
MIT License
14.68k stars 3.74k forks source link

Implement non-blocking run process #82

Closed shrx closed 8 years ago

shrx commented 8 years ago

Right now the client has be started with the client.run() command. This blocks the script so I can't manually execute commands like client.send_message()whenever I like, I am constrained to the events that the client detects. Can this be fixed?

khazhyk commented 8 years ago

you can launch your own task or thread to send messages on your own triggers. you could also run the loop in it's own thread.

shrx commented 8 years ago

@khazhyk can you provide a small example how to set up the threads? It would be very helpful. I tried with multiprocessing but I couldn't get it to work.

khazhyk commented 8 years ago

@event("ready")
def ready(client):
    '''
    Spawns the thread once we are connected to the Server
    '''
    Thread(target=gamestatus, args=(client,)).start()

def gamestatus(client):
    '''
    The thread.
    '''
    while True:
        client.change_status(game=discord.Game(name=GAMES[random.randrange(0, len(GAMES))]))
        time.sleep(1800)

etc. note: in 0.9.x ready may fire multiple times as there is auto-reconnecting code, which the above example doesn't take into account.

For 0.10.0 you don't need to use threads, you can use ensure_future somewhere to schedule a task

shrx commented 8 years ago

I don't understand. This executes a command every 1000 seconds. It doesn't return the command prompt so I can't execute a command when I want. I tried putting client.run() in a thread, but it didn't work.

khazhyk commented 8 years ago

you can put client.run in a thread if you really want.

Rapptz commented 8 years ago

If you put client.run in a separate thread you have to keep the main thread from exiting somehow.

khazhyk commented 8 years ago
>>> import discord
>>> import threading
>>> c = discord.Client()
>>> c.login(username, password)
>>> mythread = threading.Thread(target=c.run)
>>> mythread.start()

that will let you keep your prompt open and send messages

this is a weird thing to do though. If you just want to send messages, you do not need to do client.run()

shrx commented 8 years ago

@khazhyk thanks for your example. Indeed, I get the command prompt back. However, when I then try to send a message with

c.send_message(list(list(c.servers)[0].channels)[0].id, "test")

I get a response:

<generator object Client.send_message at 0x108cfd410>

but no message is displayed in the appropriate channel. What's wrong?

Hornwitser commented 8 years ago

Using the discord.py library interactively through the Python console is going to be very cumbersome, and seams to be of little practical use. To use the async branch in a separate thread in the console you need to do:

>>> import discord, threading, asyncio
>>> c = discord.Client()
>>> t = threading.Thread(target=c.run, args=(username, password))
>>> t.start()

And then to call a coroutine in client (most methods in Client are coroutines), you need to do:

>>> asyncio.run_coroutine_threadsafe(c.send_message(discord.Object(id='<channel-id>'), 'Hello world'), c.loop).result()

Note that if you exit the thread running the client, by running something like the c.logout() coroutine, or you provide invalid credentials to run, then it will not work again, because run closes the default asyncio event loop.

But why on earth do you want to run the client in the Python console? If you make any changes to your scripts, you have to reload it. Just to make the client run there's a whole bunch of things to set up, and everything you code in it is lost when you exit it.