2016-04-22 89 views
2

我正在嘗試使用asyncio製作一個簡單的Slack bot,主要使用示例here爲asyncio部分,here爲Slack bot部分。Python asyncio與Slack bot

這兩個例子都是獨立工作的,但是當我將它們放在一起時,看起來我的循環不會循環:它會經歷一次然後死亡。如果info是一個長度等於1的列表,當消息在其中包含bot的聊天室中鍵入時發生,協程應該被觸發,但它永遠不會。 (所有協程現在都在嘗試打印消息,如果消息中包含「/ time」,它會讓機器人在要求的聊天室中打印時間)。鍵盤中斷也不起作用,我必須每次關閉命令提示符。

這裏是我的代碼:

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

token = "MY TOKEN" 
sc = SlackClient(token) 

@asyncio.coroutine 
def read_text(info): 
    if 'text' in info[0]: 
     print(info[0]['text']) 
     if r'/time' in info[0]['text']: 
      print(info) 
      resp = 'The time is ' + dt.datetime.strftime(dt.datetime.now(),'%H:%M:%S') 
      print(resp) 
      chan = info[0]['channel'] 
      sc.rtm_send_message(chan, resp) 


loop = asyncio.get_event_loop() 
try: 
    sc.rtm_connect() 
    info = sc.rtm_read() 
    if len(info) == 1: 
     asyncio.async(read_text(info)) 
    loop.run_forever() 

except KeyboardInterrupt: 
    pass 
finally: 
    print('step: loop.close()') 
    loop.close() 

我認爲這是循環的一部分,這壞了,因爲它似乎從來就沒給協程。所以也許一個問這個問題的簡短方式是什麼關於我的try:語句,以防止它循環,就像我跟着的asyncio示例一樣?有沒有關於它不喜歡的sc.rtm_connect()

我是新來的asyncio,所以我可能做一些愚蠢的事情。這甚至是嘗試去做這件事的最好方法嗎?最終,我想讓機器人做一些需要很長時間來計算的事情,並且我希望它能夠在那個時候保持響應,所以我認爲我需要在某種變化中使用asyncio或線程,但是我願意更好的建議。

非常感謝, 亞歷

+0

我很擔心這個問題可能過於寬廣。有什麼方法可以提出更具體的問題,或者提出一系列構成這個問題的問題? –

+0

相當確定被破壞的部分是循環,因爲它甚至從來沒有調用過程。我想一個簡短的問題是有什麼關於'sc.rtm_connect()'調用,防止asyncio循環對象像正常循環? –

回答

1

我把它改成下面的,它的工作:

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

token = "MY TOKEN"  
sc = SlackClient(token) 

@asyncio.coroutine 
def listen(): 
    yield from asyncio.sleep(1) 
    x = sc.rtm_connect() 
    info = sc.rtm_read() 
    if len(info) == 1: 
     if 'text' in info[0]: 
      print(info[0]['text']) 
      if r'/time' in info[0]['text']: 
       print(info) 
       resp = 'The time is ' + dt.datetime.strftime(dt.datetime.now(),'%H:%M:%S') 
       print(resp) 
       chan = info[0]['channel'] 
       sc.rtm_send_message(chan, resp) 

    asyncio.async(listen()) 


loop = asyncio.get_event_loop() 
try: 
    asyncio.async(listen()) 
    loop.run_forever() 

except KeyboardInterrupt: 
    pass 
finally: 
    print('step: loop.close()') 
    loop.close() 

不能完全確定爲什麼修復它,但我改變了關鍵的東西是把sc.rtm_connect()通話在協程中並使之成爲x = sc.rtm_connect()。我也在最後調用了自己的listen()函數,這看起來是讓它永遠循環的原因,因爲如果我把它拿出來,機器人沒有響應。我不知道這是不是這樣的事情應該設立的方式,但它似乎要繼續接受命令,而它的處理前面的命令,我的鬆弛聊天看起來是這樣的:

me [12:21 AM] 
/time 

[12:21] 
/time 

[12:21] 
/time 

[12:21] 
/time 

testbotBOT [12:21 AM] 
The time is 00:21:11 

[12:21] 
The time is 00:21:14 

[12:21] 
The time is 00:21:16 

[12:21] 
The time is 00:21:19 

請注意,它不會錯過我的任何/time請求,如果它不是異步執行此操作,就會發生這種情況。另外,如果有人試圖複製這個,你會注意到,如果你輸入「/」,那麼鬆弛會引起內置的命令菜單。我通過在前面輸入空格來解決這個問題。

感謝您的幫助,如果您知道更好的方法,請告訴我。這似乎不是一個非常優雅的解決方案,並且之後我用的是一個CNTRL-C鍵盤中斷結束它的機器人無法重新啓動 - 它說

Task exception was never retrieved 
future: <Task finished coro=<listen() done, defined at asynctest3.py:8> exception=AttributeError("'NoneType' object has no attribute 'recv'",)> 
Traceback (most recent call last): 
    File "C:\Users\Dell-F5\AppData\Local\Programs\Python\Python35-32\Lib\asyncio\tasks.py", line 239, in _step 
    result = coro.send(None) 
    File "asynctest3.py", line 13, in listen 
    info = sc.rtm_read() 
    File "C:\Users\Dell-F5\Envs\sbot\lib\site-packages\slackclient\_client.py", line 39, in rtm_read 
    json_data = self.server.websocket_safe_read() 
    File "C:\Users\Dell-F5\Envs\sbot\lib\site-packages\slackclient\_server.py", line 110, in websocket_safe_read 
    data += "{0}\n".format(self.websocket.recv()) 
AttributeError: 'NoneType' object has no attribute 'recv' 

我猜意味着它不打烊很好的websockets。無論如何,這只是一個煩惱,至少主要問題是固定的。

亞歷

1

使阻塞IO調用內部的協程打敗使用ASYNCIO(例如info = sc.rtm_read())的真正目的。如果您沒有選擇,請使用loop.run_in_executor在其他線程中運行阻塞呼叫。小心,但可能需要一些額外的鎖定。

不過,似乎有一些基於ASYNCIO-鬆弛客戶端庫,你可以改用:


編輯:Butterfield使用Slac k實時消息API。它甚至還提供了一個echo bot example,看起來非常像你想達到什麼目的:

import asyncio 
from butterfield import Bot 

@asyncio.coroutine 
def echo(bot, message): 
    yield from bot.post(
     message['channel'], 
     message['text'] 
    ) 

bot = Bot('slack-bot-key') 
bot.listen(echo) 
butterfield.run(bot) 
+0

啊我明白了,儘管它不會錯過命令,但是單個循環會阻止其他人執行,是嗎?感謝所有的信息,我會盡力解決。 –

+0

我不認爲Slacker使用實時方法,對吧?我認爲這意味着我無法制作一個響應發送給它的不同消息的機器人? –

+0

@AlexS看看我的編輯是否有幫助 – Vincent