2016-04-22 1 views
2

J'essaie de faire un simple bot Slack en utilisant asyncio, en utilisant largement l'exemple here pour la partie asyncio et here pour la partie bot Slack.Asyncio Python avec Slack bot

Les deux exemples fonctionnent d'eux-mêmes, mais quand je les ai mis ensemble, il semble que ma boucle ne boucle pas: elle passe une fois, puis meurt. Si info est une liste de longueur égale à 1, ce qui arrive lorsqu'un message est tapé dans une salle de discussion avec le robot, la coroutine est censée être déclenchée, mais elle ne l'est jamais. (Tout le Coroutine essaye de faire maintenant est d'imprimer le message, et si le message contient "/ heure", il obtient le bot pour imprimer l'heure dans le salon de discussion où il a été demandé). L'interruption du clavier ne fonctionne pas non plus, je dois fermer l'invite de commande à chaque fois.

Voici mon code:

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() 

Je pense qu'il est la partie de la boucle qui est brisé, car il ne semble jamais se rendre à la coroutine. Donc, peut-être une façon plus courte de poser cette question est ce qui est à propos de mon essai: déclaration qui empêche de faire une boucle comme dans l'exemple asyncio que j'ai suivi? Y at-il quelque chose à propos de sc.rtm_connect() qu'il n'aime pas? Je suis nouveau en asyncio, donc je fais probablement quelque chose de stupide. Est-ce même la meilleure façon d'essayer de s'y prendre? En fin de compte, je veux que le bot fasse des choses qui prennent du temps à calculer, et je voudrais qu'il reste réactif à ce moment-là, donc je pense que je dois utiliser asyncio ou threads dans une certaine variété, mais je suis ouvert à meilleures suggestions.

Merci beaucoup, Alex

+0

Je suis inquiet que cette question peut être trop large. Est-il possible de poser une question plus précise ou de poser une série de questions? –

+0

Assez sûr que la partie qui est cassée est la boucle, puisqu'elle n'appelle même jamais la coroutine. Je suppose qu'une question raccourcie serait là quelque chose à propos de l'appel 'sc.rtm_connect()' qui empêche l'objet boucle asyncio de boucler comme normal? –

Répondre

1

je l'ai changé à ce qui suit et cela a fonctionné:

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() 

Pas tout à fait sûr de savoir pourquoi cela résout le problème, mais les principales choses que je changé mettions la sc.rtm_connect() appel dans la coroutine et en faisant x = sc.rtm_connect(). J'appelle aussi la fonction listen() de lui-même à la fin, ce qui semble être ce qui la fait tourner à jamais, puisque le bot ne répond pas si je l'enlève. Je ne sais pas si cela est la façon dont ce genre de chose est censé être mis en place, mais il ne semble continuer à accepter les commandes alors qu'il est le traitement des commandes précédentes, mon chat jeu ressemble à ceci:

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 

Notez qu'il ne manque aucune de mes demandes /time, ce qui serait le cas s'il ne le faisait pas de façon asynchrone. En outre, si quelqu'un essaie de répliquer cela, vous remarquerez que le slack affiche le menu de commande intégré si vous tapez "/". Je me suis débrouillé en tapant un espace devant.

Merci pour l'aide, s'il vous plaît laissez-moi savoir si vous connaissez une meilleure façon de le faire. Il ne semble pas être une solution très élégante, et le robot ne peut pas être redémarré après avoir utilisé l'une interruption du clavier cntrl-c pour y mettre fin - il dit

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' 

Ce que je suppose signifie qu'il est ne pas fermer les websockets bien. Quoi qu'il en soit, c'est juste un ennui, au moins le problème principal est résolu.

Alex

1

Faire bloquer IO appels à l'intérieur d'une coroutine défaite le but même d'utiliser asyncio (par exemple info = sc.rtm_read()). Si vous n'avez pas le choix, utilisez loop.run_in_executor pour exécuter l'appel de blocage dans un thread différent. Attention cependant, un verrouillage supplémentaire pourrait être nécessaire.

Cependant, il semble qu'il ya quelques bibliothèques clientes mou à base asyncio-vous pourriez utiliser:


EDIT: Butterfield utilise le Slac k API de messagerie en temps réel. Il fournit même un echo bot example qui ressemble beaucoup à ce que vous essayez d'atteindre:

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

Ah je vois, donc bien qu'il ne manquera pas de commandes, les boucles individuelles empêcheront les autres d'être exécutées, n'est-ce pas? Merci pour toutes les informations, je vais essayer de réparer. –

+0

Je ne pense pas que Slacker utilise les méthodes en temps réel, non? Ce que je pense signifie que je ne serais pas capable de faire un bot qui répond aux différents messages qui lui sont envoyés? –

+0

@AlexS Voir si mon aide aide – Vincent