2016-04-22 57 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