Beispiel #1
0
class Irc(Greenlet):
    """
        irc connection abstraction. inherits:
        start(), join(), kill(exception=GreenletExit, block=False, timeout=None), ready(), successful(), etc
        __init__ only receives:
            tag:        "myfreenode",
            fd:         2 or None

        the other options are accessed through the following properties,
        which delegate calls to conf.get:
            servers:    [Server("server.address +1234"), ...],
            encoding:   "utf-8"
            network:    "freenode",
            nick:       "sqrl",
            username:   "******",
            password:   "******",
            nickservpassword: "******",
            realname:   "real name",
            chans:      ["#channel": {"blacklist": ["ex"]}, ...],
            scripts:    ["users", "seen", "com", "version", "wik", "title", "choice", "dic", "ex", ...],
            masters:    ["*!*@unaffiliated/squirrel", ...]
    """

    def __init__(self, tag, fd=None, me=None):
        super(Irc, self).__init__()
        self.tag = tag
        self.net = Network(fd)
        self.group = Group()
        self.logger = logging.getLogger(self.tag)
        self.logger.setLevel(1)
        self.formatter = CuteFormatter(maxbytes=400, encoding=self.encoding)
        self.connected = fd is not None
        if self.connected:
            self.me = me
        else:
            self.me = (self.nick, None, None)

    def __repr__(self):
        return u"Irc(tag=%s)" % self.tag

    ############################################################################################### core

    def _run(self):
        """
            greenlet starts here
            connect to irc, serve in a loop
            run self.disconnect() and self.onunload() on GreenletExit and die
        """
        self.onload()                               # let it fail
        try:
            while True:
                if not self.connected:
                    self.connect()                  # let it fail (should not happen, relly)
                    for chan in self.chans:
                        self.joinchan(chan)
                while True:
                    try:
                        line = self.net.getline()
                        line = line.decode(self.encoding)
                        try:
                            msg = Message(irc=self, line=line)
                        except MessageMalformed as e:
                            self.onexception(e, unexpected=True)
                            continue
                        self.onmessage(msg)
                        if type(msg) == Message and msg.command == "ping":
                            self.send("PONG %s" % msg.params[0])
                        elif type(msg) == Numeric and msg.num == 1:
                            self.me = (msg.target, None, None)
                            self.onconnected(msg)
                            self.privmsg(msg.target, "id")
                        elif msg.frommyself:
                            self.me = msg.sender
                            self.formatter.maxbytes = 512 - 7 - len("".join(self.me).encode(self.encoding))    # :nick!user@host <PRIVMSG :text>+\r\n"
                            self.logger.log(OTHER, "i am {0[0]}!{0[1]}@{0[2]} and i can send {1} bytes".format(self.me, self.formatter.maxbytes))
                    except ConnectionFailed as e:
                        self.onexception(e, unexpected=True)
                        self.disconnect()
                        break
                    except GreenletRehash:          # don't disconnect
                        raise
                    except GreenletExit:            # same as above, but disconnect
                        self.disconnect()           # let it fail (should not happen, relly)
                        raise
                    except Exception as e:
                        self.onexception(e, unexpected=True)
        finally:
            self.onunload()                         # let it fail

    def shutdown(self, exception=GreenletExit):
        """
            kills sender greenlet, all greenlets in group, oneself
            this will cause _run to exit and the thing should get deleted from memory
            ! if exception is GreenletExit, disconnects
        """
        self.group.kill(exception, block=False)
        self.kill(exception, block=False)

    ############################################################################################### my cute methods

    def send(self, data, log=True):
        """ encodes and sends one line """
        self.net.send(data.encode(self.encoding) + "\r\n")
        self.onsent(data, log)

    def connect(self):
        """ connects to servers[currectserver] """
        delay = 0
        while True:
            for server in self.servers:
                try:
                    self.onconnect(server)
                    self.net.connect(server.address, server.ssl)
                    self.send(u"NICK {0.nick}".format(self))
                    self.send(u"USER {0.username} lol wut :{0.realname}".format(self))
                    self.connected = True
                    return
                except ConnectionFailed as e:
                    self.onexception(e)
                    sleep(10 + delay)
                    delay += 1

    def disconnect(self):
        """ good-bye! """
        try:
            self.ondisconnect()
        finally:
            self.net.disconnect()
            self.connected = False

    def notice(self, target, line, *args, **kwargs):
        line = self._format(u"NOTICE {target} :{line}", target, line, *args, **kwargs)
        self.send(line, False)
        self.onsentnotice(target, line[9 + len(target):])

    def privmsg(self, target, line, nick=None, *args, **kwargs):
        command = u"PRIVMSG {target} :{line}" if nick is None else u"PRIVMSG {target} :%s: {line}" % nick
        line = self._format(command, target, line, *args, **kwargs)
        self.send(line, False)
        self.onsentprivmsg(target, line[10 + len(target):])

    def action(self, target, line, *args, **kwargs):
        line = self._format(u"PRIVMSG {target} :\x01ACTION {line}\x01", target, line, *args, **kwargs)
        self.send(line, False)
        self.onsentaction(target, line[18 + len(target):])

    def joinchan(self, chan):
        self.send(u"JOIN " + chan)

    def _format(self, command, target, line, *args, **kwargs):
        kwargs["lang"] = conf.getdefault("lang", self.tag, target, "en")
        line = self.formatter.translate_format_string(line, **kwargs)
        return self.formatter.format(command.format(target=target, line=line), *args, **kwargs)
    ############################################################################################### replace me

    def onconnect(self, server):
        """ called when we *start* connecting """
        self.logger.log(OTHER, "connecting to %s..." % server)

    def onconnected(self, msg):
        """ called when we have successfully connected to a server """
        self.logger.log(OTHER, "connected to %s!" % msg.sender[0])

    def ondisconnect(self):
        """ called when we disconnect """
        self.logger.log(OTHER, "disconnecting")

    def onload(self):
        """ called when the thing starts """
        self.logger.log(OTHER, "loading")

    def onunload(self):
        """ called when the thing dies """
        self.logger.log(OTHER, "unloading")

    def onexception(self, e, unexpected=False):
        """ called when any bot's internal exception occurs (the exception gets handled) """
        if unexpected:
            self.logger.exception(unicode(e))
        else:
            self.logger.error(unicode(e))

    ###

    def onmessage(self, msg):
        """ called when the thing receives any irc message """
        if type(msg) == Privmsg:
            self.onprivmsg(msg)
        elif type(msg) == Action:
            self.onaction(msg)
        else:
            self.logger.log(IN, msg.line)

    def onprivmsg(self, msg):
        dt = msg.sender[0] if msg.target == self.me[0] else msg.target
        self.logger.log(IN_PRIVMSG, "%s | <%s> %s", dt, msg.sender[0], msg.message)

    def onaction(self, msg):
        self.logger.log(IN_ACTION, "%s | * %s %s", msg.target, msg.sender[0], msg.message)

    ###

    def onsent(self, text, unprocessed):
        """ called on every send. if unprocessed is True, onsenetprivmsg and the sech have not been called """
        if unprocessed:
            self.logger.log(OUT, text)

    def onsentprivmsg(self, target, text):
        self.logger.log(OUT_PRIVMSG, "%s | <%s> %s", target, self.me[0], text)

    def onsentnotice(self, target, text):
        self.logger.log(OUT_NOTICE, "%s | <%s> %s", target, self.me[0], text)

    def onsentaction(self, target, text):
        self.logger.log(OUT_ACTION, "%s | * %s %s", target, self.me[0], text)