对不起,如果我的标题错了,我是Twisted的新手并不能真正描述我的问题。
所以问题是,我有一个基于ircLogBot.py(http://twistedmatrix.com/documents/current/words/examples/ircLogBot.py)的IRC bot,它通过PHP页面在IRC和MySQL数据库之间中继消息。
它必须每1秒加载一个PHP页面,解析内容(JSON),循环遍历它,然后将每个项目发布到IRC。除了将其发布到IRC之外,我已将其全部排序。
它很难的原因是因为循环在另一个线程内运行(它必须工作)而且我不知道如何从该线程调用msg()。
这个描述可能真的令人困惑,所以看看我的代码。我评论了我要发送信息的位置:
from twisted.words.protocols import irc
from twisted.internet import reactor, protocol, threads
from twisted.python import log
# system imports
import time, sys, json, urllib, urllib2, threading
url = 'http://86.14.76.169/root/scripts/ircbot.php'
urltwo = 'http://86.14.76.169/root/scripts/returnchat.php'
class LogBot(irc.IRCClient):
try:
"""A logging IRC bot."""
nickname = "WorldConflictBot"
def connectionMade(self):
irc.IRCClient.connectionMade(self)
def connectionLost(self, reason):
irc.IRCClient.connectionLost(self, reason)
# callbacks for events
def signedOn(self):
"""Called when bot has succesfully signed on to server."""
self.join(self.factory.channel)
def joined(self, channel):
"""This will get called when the bot joins the channel."""
self.action('JackBot', self.factory.channel, 'Joined')
def privmsg(self, user, channel, msg):
"""This will get called when the bot receives a message."""
user = user.split('!', 1)[0]
values = {}
values = {'type' : 'message',
'message' : msg,
'username' : user,
}
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
the_page = response.read()
# Check to see if they're sending me a private message
if channel == self.nickname:
msg = "It isn't nice to whisper! Play nice with the group."
self.msg(user, msg)
return
# Otherwise check to see if it is a message directed at me
if msg.startswith(self.nickname + ":"):
msg = "%s: Hey :)" % user
self.msg(channel, msg)
def action(self, user, channel, msg):
"""This will get called when the bot sees someone do an action."""
user = user.split('!', 1)[0]
# irc callbacks
def irc_NICK(self, prefix, params):
"""Called when an IRC user changes their nickname."""
old_nick = prefix.split('!')[0]
new_nick = params[0]
values = {}
values = {'type' : 'nick',
'from' : old_nick,
'to' : new_nick,
}
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
the_page = response.read()
# For fun, override the method that determines how a nickname is changed on
# collisions. The default method appends an underscore.
def alterCollidedNick(self, nickname):
"""
Generate an altered version of a nickname that caused a collision in an
effort to create an unused related name for subsequent registration.
"""
return nickname + '^'
except KeyboardInterrupt:
LogBotLooper.exit()
sys.exit()
class LogBotFactory(protocol.ClientFactory):
"""A factory for LogBots.
A new protocol instance will be created each time we connect to the server.
"""
def __init__(self):
self.channel = 'worldconflict'
def buildProtocol(self, addr):
p = LogBot()
p.factory = self
return p
l = LogBotLooper()
l.factory = self
return l
def clientConnectionLost(self, connector, reason):
"""If we get disconnected, reconnect to server."""
connector.connect()
def clientConnectionFailed(self, connector, reason):
print "connection failed:", reason
reactor.stop()
class LogBotLooper(irc.IRCClient):
def __init__(self):
i = 0
lastid = 0
while 1:
time.sleep(1)
if(i == 0):
values = {'justlastid': 'true'}
else:
values = {'lastid' : lastid}
data = urllib.urlencode(values)
req = urllib2.Request(urltwo, data)
response = urllib2.urlopen(req)
the_page = response.read()
if(i == 0):
lastid = the_page
i += 1
else:
if(the_page != 'error'):
jsonpage = json.loads(the_page)
for message in jsonpage['messages']:
#Need to send the variable `message` to IRC.
lastid = jsonpage['highestid']
def exit(self):
sys.exit()
if __name__ == '__main__':
try:
# initialize logging
log.startLogging(sys.stdout)
# create factory protocol and application
f = LogBotFactory()
# connect factory to this host and port
reactor.connectTCP("irc.skyirc.net", 6667, f)
reactor.callInThread(LogBotLooper)
# run bot
reactor.run()
except KeyboardInterrupt:
LogBotLooper.exit()
sys.exit()
答案 0 :(得分:2)
您可能已经知道这一点,但您的协议类应该只是坚持事件处理和传输本身的其他抽象。这样你就可以保持关注点的分离,并且你有一个可维护的框架。在MVC范例中,您的协议类是控制器,甚至可能是视图,但绝对不是模型。进行PHP Web服务调用可能属于模型。
至于将工作转移到其他线程(对于任何阻塞I / O,例如Web服务调用,您肯定需要这样做),您需要:
from twisted.internet import threads, reactor
从主反应堆主题,调用threads.deferToThread(mycallable, *args, **kwargs)
从下一个可用的工作线程调用mycallable
。
从任何工作线程,调用reactor.callFromThread(mycallable, *args, **kwargs)
从主反应堆线程调用mycallable
。
要将工作从一个工作线程转移到另一个工作线程,请结合使用以下两种技术:reactor.callFromThread(threads.deferToThread, mycallable, *args, **kwargs)
。
我相信这两个调用都会返回一个Deferred
对象(我知道deferToThread
会这样做)。如果向延迟添加回调,那些回调将在与原始callable相同的线程中执行。要将回调执行委派给工作线程,请在回调中使用上述技术。 (他们并没有把它称为“扭曲”。)
答案 1 :(得分:1)
如果我的帖子没有收到错误信息,我会遇到与您相同的问题。
http://twistedmatrix.com/documents/10.1.0/core/howto/threading.html
threads.blockingCallFromThread 是此问题的另一个答案。
只需替换
#Need to send the variable `message` to IRC.
与
threads.blockingCallFromThread(reactor, irc.send_message, message)
#I assume you call irc.send_message here