我正在尝试编写一个IRC机器人,该机器人在执行较长的功能(10秒钟以上)时仍可继续正常工作。
我首先使用套接字编写机器人。当我调用“阻止”功能(执行该过程需要几秒钟的计算)时,该机器人自然会停止响应,并且在该功能进行计算时不会记录聊天中发送的任何消息。
我做了一次谷歌搜索,看到很多人推荐使用Twisted。
我主要基于一些示例实现了基本的IRC bot:
# twisted imports
from twisted.words.protocols import irc
from twisted.internet import reactor, protocol
from twisted.python import log
# system imports
import time, sys, datetime
def a_long_function():
time.sleep(180)
print("finished")
class BotMain(irc.IRCClient):
nickname = "testIRC_bot"
def connectionMade(self):
irc.IRCClient.connectionMade(self)
def connectionLost(self, reason):
irc.IRCClient.connectionLost(self, reason)
# callbacks for events
def signedOn(self):
"""Signed to server"""
self.join(self.factory.channel)
def joined(self, channel):
"""Joined channel"""
def privmsg(self, user, channel, msg):
"""Received message"""
user = user.split('!', 1)[0]
if 'test' in msg.lower():
print("timeout started")
a_long_function()
msg = "test finished"
self.msg(channel, msg)
if 'ping' in msg.lower():
self.msg(channel, "pong")
print("pong")
class BotMainFactory(protocol.ClientFactory):
"""A factory for BotMains """
protocol = BotMain
def __init__(self, channel, filename):
self.channel = channel
self.filename = filename
def clientConnectionLost(self, connector, reason):
"""Try to reconnect on connection lost"""
connector.connect()
def clientConnectionFailed(self, connector, reason):
print ("connection failed:", reason)
reactor.stop()
if __name__ == '__main__':
log.startLogging(sys.stdout)
f = BotMainFactory("#test", "log.txt")
reactor.connectTCP("irc.freenode.net", 6667, f)
reactor.run()
这种方法绝对比我以前的套接字实现更好,因为现在该机器人在执行 a_long_function()时仍可以接收发送的消息。
但是,它仅在功能完成后才“看到”这些消息。这意味着当我将消息记录到txt文件时,在 a_long_function()执行时收到的所有消息都收到与函数完成时间相同的时间戳,而不是在聊天室中实际发送的时间戳
此外,该机器人在执行long函数时仍然无法发送任何消息。
有人可以向我指出更改代码的正确方向,以便可以异步执行此长函数,以便机器人在执行过程中仍可以记录并回复消息吗?
谢谢。
编辑: 我遇到了this答案,这使我想到可以在我的 a_long_function 中添加 deferLater 调用,将其拆分为较小的块(即1s执行),然后让漫游器恢复正常运行,以回复并记录同时发送到IRC通道的所有消息。或者,也许添加一个计时器来计算 a_long_function 的运行时间,如果该计时器长于阈值,它将调用 deferLater 来让机器人赶上缓冲的邮件。
这似乎有点骇人听闻-有更好的解决方案吗?
答案 0 :(得分:0)
不,实际上没有更好的解决方案。除非您要使用线程,否则外观可能会更优雅,但很容易导致程序不稳定。如果可以避免,请采用延迟解决方案。
答案 1 :(得分:0)
要异步调用函数,应将asyncio包与async / await或协程一起使用。请记住,调用async / await是v3的实现,而不是v2。
使用异步/等待:
storage.objects.list
有一个非常不错的教程,您可以阅读here,深入了解asyncio。
希望有帮助!