Python asyncio 与 Slack 机器人
Posted
技术标签:
【中文标题】Python asyncio 与 Slack 机器人【英文标题】:Python asyncio with Slack bot 【发布时间】:2016-08-15 13:15:34 【问题描述】:我正在尝试使用 asyncio 制作一个简单的 Slack 机器人,主要使用示例 here 用于 asyncio 部分,here 用于 Slack 机器人部分。
这两个示例都可以单独运行,但是当我将它们放在一起时,我的循环似乎没有循环:它通过一次然后死掉。如果info
是一个长度等于 1 的列表,当在聊天室中输入一条消息时会发生这种情况,其中包含机器人,则应该触发协程,但它永远不会触发。 (协程现在要做的就是打印消息,如果消息包含“/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 或各种线程,但我愿意更好的建议。
非常感谢, 亚历克斯
【问题讨论】:
我担心这个问题可能过于宽泛。有没有什么方法可以问一个更具体的问题,或者问一系列构成这个问题的问题? 很确定被破坏的部分是循环,因为它甚至从不调用协程。我想一个简短的问题是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
请求,如果它不异步执行这些操作,它会丢失。此外,如果有人试图复制这个,你会注意到,如果你输入“/”,slack 会打开内置的命令菜单。我通过在前面输入一个空格来解决这个问题。
感谢您的帮助,如果您知道更好的方法,请告诉我。这似乎不是一个非常优雅的解决方案,并且在我使用 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'
我猜这意味着它没有很好地关闭 websocket。不管怎样,这只是一个烦恼,至少主要问题已经解决了。
亚历克斯
【讨论】:
【参考方案2】:在协程中进行阻塞 IO 调用违背了使用 asyncio 的目的(例如info = sc.rtm_read()
)。如果您别无选择,请使用loop.run_in_executor 在不同的线程中运行阻塞调用。不过要小心,可能需要一些额外的锁定。
但是,您似乎可以使用一些基于 asyncio 的 slack 客户端库来代替:
slacker-asyncio - slacker 的分支,基于 aiohttp butterfield - 基于 slacker 和 websockets编辑:Butterfield 使用 Slack 实时消息 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)
【讨论】:
啊,我明白了,虽然它不会错过命令,但个别循环会阻止其他循环被执行,对吗?感谢您提供所有信息,我会尽力解决。 我不认为 Slacker 使用实时方法,对吧?我认为这意味着我无法制作一个机器人来响应发送给它的不同消息? 谢谢,我会去巴特菲尔德。我有点担心它似乎不是很发达,而且自去年年初以来也没有太多的工作。以上是关于Python asyncio 与 Slack 机器人的主要内容,如果未能解决你的问题,请参考以下文章
Slack 机器人可以与另一个机器人交互并在频道中触发它的某些功能吗?