简体   繁体   中英

Making a Python slack bot asynchronous

I've been trying to make a bot in Slack that remains responsive even if it hasn't finished processing earlier commands, so it could go and do something that takes some time without locking up. It should return whatever is finished first.

I think I'm getting part of the way there: it now doesn't ignore stuff that's typed in before an earlier command is finished running. But it still doesn't allow threads to "overtake" each other - a command called first will return first, even if it takes much longer to complete.

import asyncio
from slackclient import SlackClient
import time, datetime as dt

token = "my token"
sc = SlackClient(token)

@asyncio.coroutine
def sayHello(waitPeriod = 5):
    yield from asyncio.sleep(waitPeriod)
    msg = 'Hello! I waited {} seconds.'.format(waitPeriod)
    return msg

@asyncio.coroutine
def listen():
    yield from asyncio.sleep(1)
    x = sc.rtm_connect()
    info = sc.rtm_read()
    if len(info) == 1:
            if r'/hello' in info[0]['text']:
                print(info)
                try:
                    waitPeriod = int(info[0]['text'][6:])
                except:
                    print('Can not read a time period. Using 5 seconds.')
                    waitPeriod = 5
                msg = yield from sayHello(waitPeriod = waitPeriod)
                print(msg)
                chan = info[0]['channel']
                sc.rtm_send_message(chan, msg)      

    asyncio.async(listen())

def main():
    print('here we go')
    loop = asyncio.get_event_loop()
    asyncio.async(listen())
    loop.run_forever()

if __name__ == '__main__':
    main()

When I type /hello 12 and /hello 2 into the Slack chat window, the bot does respond to both commands now. However it doesn't process the /hello 2 command until it's finished doing the /hello 12 command. My understanding of asyncio is a work in progress, so it's quite possible I'm making a very basic error. I was told in a previous question that things like sc.rtm_read() are blocking functions. Is that the root of my problem?

Thanks a lot, Alex

What is happening is your listen() coroutine is blocking at the yield from sayHello() statement. Only once sayHello() completes will listen() be able to continue on its merry way. The crux is that the yield from statement (or await from Python 3.5+) is blocking. It chains the two coroutines together and the 'parent' coroutine can't complete until the linked 'child' coroutine completes. (However, 'neighbouring' coroutines that aren't part of the same linked chain are free to proceed in the meantime).

The simple way to release sayHello() without holding up listen() in this case is to use listen() as a dedicated listening coroutine and to offload all subsequent actions into their own Task wrappers instead, thus not hindering listen() from responding to subsequent incoming messages. Something along these lines.

@asyncio.coroutine
def sayHello(waitPeriod, sc, chan):
    yield from asyncio.sleep(waitPeriod)
    msg = 'Hello! I waited {} seconds.'.format(waitPeriod)
    print(msg)
    sc.rtm_send_message(chan, msg)   

@asyncio.coroutine
def listen():
    # connect once only if possible:
    x = sc.rtm_connect()
    # use a While True block instead of repeatedly calling a new Task at the end
    while True:
        yield from asyncio.sleep(0)  # use 0 unless you need to wait a full second?
        #x = sc.rtm_connect() # probably not necessary to reconnect each loop?
        info = sc.rtm_read()
        if len(info) == 1:
                if r'/hello' in info[0]['text']:
                    print(info)
                    try:
                        waitPeriod = int(info[0]['text'][6:])
                    except:
                        print('Can not read a time period. Using 5 seconds.')
                        waitPeriod = 5
                    chan = info[0]['channel']
                    asyncio.async(sayHello(waitPeriod, sc, chan))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM