Я пытаюсь написать IRC-бота, который продолжает нормально работать, пока выполняет длинную (более 10 секунд) функцию.
Я начал с написания бота с помощью сокета.Когда я вызвал «блокирующую» функцию (вычисление, для выполнения которой требуется несколько секунд), бот, естественно, перестал отвечать и не записывал никаких сообщений, отправленных в чат, пока функция выполняла вычисления.
Я немного погуглил и увидел,многие люди рекомендуют использовать Twisted.
Я реализовал базовый бот IRC, в значительной степени основанный на некоторых примерах:
# 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 () , получали одинаковую отметку времени завершения функции, а не когда они фактически были отправлены в чате.
Кроме того, бот по-прежнему не может отправлять сообщения во время выполнения длинной функции.
Может ли кто-нибудь указать мне правильное направление, как мне следует изменить код так,что эта длинная функция может быть выполнена асинхронно, так что бот все еще может регистрировать и отвечать на сообщения во время выполнения?
Заранее спасибо.
Редактировать: я сталкивался с этим ответ, который дал мне идею, что я мог бы добавить deferLater вызовов в мои a_long_function , чтобы разделить его на более мелкие куски (например, чтобы выполнить 1 с), и получить возобновление работы ботаобычная работа между ответами и регистрацией любых сообщений, которые были отправлены на канал IRC за это время.Или, возможно, добавить таймер, который подсчитывает, как долго работает a_long_function , и, если он длиннее порога, он будет вызывать deferLater , чтобы позволить боту догнать буферизованные сообщения.
Это похоже на хакерскую мысль - есть ли более элегантное решение?