Twisted / Python - вызов метода внутри протокола из другого потока - PullRequest
0 голосов
/ 30 марта 2012

извините, если я неправильно понял название, я новичок в Twisted и не могу точно описать мою проблему.

Итак, проблема в том, что у меня есть бот IRC, основанный на ircLogBot.py (http://twistedmatrix.com/documents/current/words/examples/ircLogBot.py), который передает сообщения между IRC и базой данных MySQL через страницу PHP.

Он должен загружать страницу PHP каждую 1 секунду, анализировать содержимое (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()

Ответы [ 2 ]

2 голосов
/ 30 марта 2012

Вы, наверное, уже знали это, но ваш класс протокола должен просто придерживаться обработки событий и других абстракций над самим транспортом.Таким образом, вы сохраняете разделение интересов и имеете поддерживаемую структуру.В парадигме MVC ваш класс протокола является контроллером или, может быть, даже представлением, но определенно не моделью.Выполнение вызовов веб-службы PHP, вероятно, относится к этой модели.

Для передачи работы другим потокам (что вам определенно потребуется для любого блокирующего ввода-вывода, такого как вызовы веб-службы), вам необходимо:

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 делает).Если вы добавите обратные вызовы к отложенному, эти обратные вызовы будут выполняться в том же потоке, что и исходный вызываемый объект.Чтобы делегировать выполнение обратного вызова рабочим потокам, используйте описанные выше методы в обратных вызовах.(Они не зря называют это «витой».)

1 голос
/ 28 июня 2012

Если я не получил неправильное сообщение из вашего поста, у меня та же проблема, что и у вас.

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
...