• Facebook
  • Twitter
  • Reddit
  • StumbleUpon
  • Digg
  • email

# gozerlib/socket/irc/irc.py
#
#
 
""" 
    an Irc object handles the connection to the irc server .. receiving,
    sending, connect and reconnect code.
 
"""
 
## gozerlib imports
 
from gozerlib.utils.exception import handle_exception
from gozerlib.socket.utils.generic import getrandomnick, toenc, fromenc, strippedtxt
from gozerlib.socket.utils.generic import fix_format, splittxt, waitforqueue, uniqlist
from gozerlib.utils.locking import lockdec
from gozerlib.config import cfg as config
from gozerlib.datadir import datadir
from gozerlib.botbase import BotBase
from gozerlib.threads import start_new_thread, threaded
from gozerlib.utils.pdod import Pdod
from gozerlib.channelbase import ChannelBase
from gozerlib.morphs import inputmorphs, outputmorphs
 
## gozerlib.irc imports
 
from ircevent import Ircevent
from monitor import saymonitor
from wait import Wait
 
## basic imports
 
import time
import thread
import socket
import threading
import os
import Queue
import random
import logging
 
## locks
 
outlock = thread.allocate_lock()
outlocked = lockdec(outlock)
 
## exceptions
 
class AlreadyConnected(Exception):
 
    """ already connected exception """
 
    pass
 
class AlreadyConnecting(Exception):
 
    """ bot is already connecting exception """
 
    pass
 
class Irc(BotBase):
 
    """ the irc class, provides interface to irc related stuff. """
 
    def __init__(self, cfg=None, users=None, plugs=None, *args, **kwargs):
        BotBase.__init__(self, cfg, users, plugs, *args, **kwargs)
        BotBase.setstate(self)
        self.type = 'irc'
        self.wait = Wait()
        self.outputlock = thread.allocate_lock()
        self.fsock = None
        self.oldsock = None
        self.sock = None
        if self.cfg:
            if not self.cfg.has_key('nolimiter'):
                self.nolimiter = 0
        else:
            self.nolimiter = self.cfg['nolimiter']
        self.reconnectcount = 0
        self.pongcheck = 0
        self.nickchanged = 0
        self.noauto433 = 0
        if self.state:
            if not self.state.has_key('alternick'):
                self.state['alternick'] = self.cfg['alternick']
            if not self.state.has_key('no-op'):
                self.state['no-op'] = []
        self.nrevents = 0
        self.gcevents = 0
        self.outqueues = [Queue.Queue() for i in range(10)]
        self.tickqueue = Queue.Queue()
        self.nicks401 = []
        self.stopreadloop = False
        self.stopoutloop = False
        if self.port == 0:
            self.port = 6667
        self.connectlock = thread.allocate_lock()
        self.connectok = threading.Event()
        self.encoding = 'utf-8'
        self.blocking = 1
 
    def __del__(self):
        self.exit()
 
    def _raw(self, txt):
 
        """ send raw text to the server. """
 
        if not txt:
            return
 
        logging.debug("irc - sending %s" % txt)
 
        try:
            self.lastoutput = time.time()
            itxt = toenc(outputmorphs.do(txt), self.encoding)
            if self.cfg.has_key('ssl') and self.cfg['ssl']:
                self.sock.write(itxt + '\n')
            else:
                self.sock.send(itxt[:500] + '\n')
        except Exception, ex:
            # check for broken pipe error .. if so ignore 
            # used for nonblocking sockets
            try:
                (errno, errstr) = ex
                if errno != 32 and errno != 9:
                    raise
                else:
                    time.sleep(0.5)
            except:
                logging.warn("irc - ERROR: can't send %s" % str(ex))
                self.reconnect()
 
    def _connect(self):
 
        """ connect to server/port using nick. """
 
        if self.connecting:
            raise AlreadyConnecting()
 
        if self.connected:
            raise AlreadyConnected()
 
        self.stopped = 0
        self.connecting = True
        self.connectok.clear()
        self.connectlock.acquire()
 
        # create socket
        if self.ipv6:
            self.oldsock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
            self.ipv6 = 1
        else:
            self.oldsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
        assert(self.oldsock)
 
        # optional bind
        server = self.server
        elite = self.cfg['bindhost'] or config['bindhost']
        if elite:
            try:
                self.oldsock.bind((elite, 0))
            except socket.gaierror:
                logging.info("irc - %s - can't bind to %s" % (self.name, elite))
               # resolve the IRC server and pick a random server
                if not server:
                    # valid IPv6 ip?
                    try: socket.inet_pton(socket.AF_INET6, self.server)
                    except socket.error: pass
                    else: server = self.server
                if not server:  
                    # valid IPv4 ip?
                    try: socket.inet_pton(socket.AF_INET, self.server)
                    except socket.error: pass
                    else: server = self.server
                if not server:
                    # valid hostname?
                    ips = []
                    try:
                        for item in socket.getaddrinfo(self.server, None):
                            if item[0] in [socket.AF_INET, socket.AF_INET6] and item[1] == socket.SOCK_STREAM:
                                ip = item[4][0]
                                if ip not in ips: ips.append(ip)
                    except socket.error: pass
                    else: server = random.choice(ips)
 
        # do the connect .. set timeout to 30 sec upon connecting
        logging.warn('irc - connecting to %s (%s)' % (server, self.server))
        self.oldsock.settimeout(15)
        self.oldsock.connect((server, int(self.port)))
 
        # we are connected
        logging.warn('irc - connection ok')
        time.sleep(1)
        self.connected = True
 
        # make file socket
        self.fsock = self.oldsock.makefile("r")
 
        # set blocking
        self.oldsock.setblocking(self.blocking)
        self.fsock._sock.setblocking(self.blocking)
 
        # set socket time out
        if self.blocking:
            socktimeout = self.cfg['socktimeout']
            if not socktimeout:
                socktimeout = 301.0
            else:
                socktimeout = float(socktimeout)
            self.oldsock.settimeout(socktimeout)
            self.fsock._sock.settimeout(socktimeout)
        # enable ssl if set
        if self.cfg.has_key('ssl') and self.cfg['ssl']:
            logging.info('irc - ssl enabled')
            self.sock = socket.ssl(self.oldsock) 
        else:
            self.sock = self.oldsock
 
        # try to release the outputlock
        try:
            self.outputlock.release()
        except thread.error:
            pass
 
        return 1
 
    def start(self):
        self._connect()
        # start input and output loops
        start_new_thread(self._readloop, ())
        start_new_thread(self._outloop, ())
 
        # logon and start monitor
        self._logon()
        self.nickchanged = 0
        self.reconnectcount = 0
        saymonitor.start()
        self.connectok.wait()
        logging.warn("irc - logged on!")
        return 1
 
    def _readloop(self):
 
        """ loop on the socketfile. """
 
        self.stopreadloop = 0
        self.stopped = 0
        doreconnect = 0
        timeout = 1
        logging.debug('irc - starting readloop')
        prevtxt = ""
 
        while not self.stopped and not self.stopreadloop:
 
            try:
                time.sleep(0.01)
                if self.cfg.has_key('ssl') and self.cfg['ssl']:
                    intxt = inputmorphs.do(self.sock.read()).split('\n')
                else:
                    intxt = inputmorphs.do(self.fsock.readline()).split('\n')
                # if intxt == "" the other side has disconnected
                if self.stopreadloop or self.stopped:
                    doreconnect = 0
                    break
                if not intxt or not intxt[0]:
                    doreconnect = 1
                    break
                if prevtxt:
                    intxt[0] = prevtxt + intxt[0]
                    prevtxt = ""
                if intxt[-1] != '':
                    prevtxt = intxt[-1]
                    intxt = intxt[:-1]
                for r in intxt:
                    r = r.rstrip()
                    rr = fromenc(r, self.encoding)
                    if not rr:
                        continue
                    res = strippedtxt(rr)
                    res = rr
                    logging.debug(u"irc - %s" % res)
                    # parse txt read into an ircevent
                    try:
                        ievent = Ircevent().parse(self, res)
                    except Exception, ex:
                        handle_exception()
                        continue
                    # call handle_ievent 
                    if ievent:
                        self.handle_ievent(ievent)
                    timeout = 1
 
            except UnicodeError:
                handle_exception()
                continue
 
            except socket.timeout:
                # timeout occured .. first time send ping .. reconnect if
                # second timeout follows
                if self.stopped:
                    break
                timeout += 1
                if timeout > 2:
                    doreconnect = 1
                    logging('irc - no pong received')
                    break
                logging.debug("irc - socket timeout")
                pingsend = self.ping()
                if not pingsend:
                    doreconnect = 1
                    break
                continue
 
            except socket.sslerror, ex:
                # timeout occured .. first time send ping .. reconnect if
                # second timeout follows
                if self.stopped or self.stopreadloop:
                    break
                if not 'timed out' in str(ex):
                    handle_exception()
                    doreconnect = 1
                    break
                timeout += 1
                if timeout > 2:
                    doreconnect = 1
                    logging.info('irc - no pong received')
                    break
                logging.error("irc - socket timeout")
                pingsend = self.ping()
                if not pingsend:
                    doreconnect = 1
                    break
                continue
 
            except IOError, ex:
                if self.blocking and 'temporarily' in str(ex):
                    time.sleep(0.5)
                    continue
                handle_exception()
                reconnect = 1
                break
 
            except Exception, ex:
                if self.stopped or self.stopreadloop:
                    break
                err = ex
                try:
                    (errno, msg) = ex
                except:
                    errno = -1
                    msg = err
                # check for temp. unavailable error .. raised when using
                # nonblocking socket .. 35 is FreeBSD 11 is Linux
                if errno == 35 or errno == 11:
                    time.sleep(0.5)
                    continue
                logging.error("irc - error in readloop: %s" % msg)
                doreconnect = 1
                break
 
        logging.error('irc - readloop stopped')
        self.connectok.clear()
        self.connected = False
 
        # see if we need to reconnect
        if doreconnect:
            time.sleep(2)
            self.reconnect()
 
    def _getqueue(self):
 
        """ get one of the outqueues. """
 
        go = self.tickqueue.get()
        for index in range(len(self.outqueues)):
            if not self.outqueues[index].empty():
                return self.outqueues[index]
 
    def putonqueue(self, nr, *args):
 
        """ put output onto one of the output queues. """
 
        self.outqueues[nr].put_nowait(*args)
        self.tickqueue.put_nowait('go')
 
    def _outloop(self):
 
        """ output loop. """
 
        logging.debug('irc - starting output loop')
        self.stopoutloop = 0
 
        while not self.stopped and not self.stopoutloop:
            queue = self._getqueue()
            if queue:
                try:
                    res = queue.get_nowait()
                except Queue.Empty:
                    continue
                if not res:
                    continue
                try:
                    (printto, what, who, how, fromm, speed) = res
                except ValueError:
                    self.send(res)
                    continue
                if not self.stopped and not self.stopoutloop and printto \
not in self.nicks401:
                    self.out(printto, what, who, how, fromm, speed)
            else:
                time.sleep(0.1)
 
        logging.debug('irc - stopping output loop')
 
    def _logon(self):
 
        """ log on to the network. """
 
        # if password is provided send it
        if self.password:
            logging.warn('irc - sending password')
            self._raw("PASS %s" % self.password)
 
        # register with irc server
        logging.warn('irc - registering with %s using nick %s' % (self.server, self.nick))
        logging.warn('irc - this may take a while')
 
        # check for username and realname
        username = self.nick or self.cfg['username']
        realname = self.cfg['realname'] or username
 
        # first send nick
        time.sleep(1)
        self._raw("NICK %s" % self.nick)
        time.sleep(1)
 
        # send USER
        self._raw("USER %s localhost localhost :%s" % (username, \
realname))
 
        # wait on login
        self.connectok.wait()
 
    def _onconnect(self):
 
        """ overload this to run after connect. """
 
        pass
 
    def _resume(self, data, reto=None):
 
        """ resume to server/port using nick. """
 
        try:
            if data['ssl']:
                self.connectwithjoin()
                return 1
        except KeyError:
            pass
 
        try:
            fd = int(data['fd'])
        except (TypeError, ValueError):
            fd = None
 
        self.connecting = False # we're already connected
        self.nick = data['nick']
        self.orignick = self.nick
        self.server = str(data['server'])
        self.port = int(data['port'])
        self.password = data['password']
        self.ipv6 = data['ipv6']
        self.ssl = data['ssl']
 
        # create socket
        if self.ipv6:
            if fd:
                self.sock = socket.fromfd(fd , socket.AF_INET6, socket.SOCK_STREAM)
            else:
                self.sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
            self.ipv6 = 1
        else:
            if fd:
                self.sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
            else:
                self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
        # do the connect .. set timeout to 30 sec upon connecting
        logging.info('irc - resuming to ' + self.server)
        self.sock.settimeout(30)
        self.stopped = 0
        # make file socket
        self.fsock = self.sock.makefile("r")
        # set blocking
        self.sock.setblocking(self.blocking)
 
        # set socket time out
        if self.blocking:
            socktimeout = self.cfg['socktimeout']
            if not socktimeout:
                socktimeout = 301.0
            else:
                socktimeout = float(socktimeout)
            self.sock.settimeout(socktimeout)
 
        # start readloop
        logging.debug('resuming readloop')
        start_new_thread(self._readloop, ())
        start_new_thread(self._outloop, ())
 
        # init 
        self.reconnectcount = 0
        self.nickchanged = 0
        self.connecting = False
 
        # still there server?
        self._raw('PING :RESUME %s' % str(time.time()))
        self.connectok.set()
        self.connected = True
        self.reconnectcount = 0
        if reto:
            self.say(reto, 'rebooting done')
        saymonitor.start()
        return 1
 
    def _resumedata(self):
 
        """ return data used for resume. """
        try:
            fd = self.sock.fileno()
        except AttributeError, ex:
            fd = None
            self.exit()
        return {self.name: {
            'nick': self.nick,
            'server': self.server,
            'port': self.port,
            'password': self.password,
            'ipv6': self.ipv6,
            'ssl': self.ssl,
            'fd': fd
            }}
 
 
    def outputsizes(self):
 
        """ return sizes of output queues. """
 
        result = []
        for q in self.outqueues:
            result.append(q.qsize())
        return result
 
    def broadcast(self, txt):
 
        """ broadcast txt to all joined channels. """
 
        for i in self.state['joinedchannels']:
            self.say(i, txt, speed=-1)
 
    def save(self):
 
        """ save state data. """
 
        if self.state:
            self.state.save()
 
    def connect(self, reconnect=True):
 
        """ connect to server/port using nick .. connect can timeout so catch
            exception .. reconnect if enabled.
        """
 
        res = 0
 
        try:
            res = self._connect()
            if res:
                self.connectok.wait()
                self._onconnect()
                self.connected = True
                logging.warn('logged on !')
            self.connecting = False
        except AlreadyConnecting:
            return 0 
        except AlreadyConnected:
            return 0
        except Exception, ex:
            self.connectlock.release()
            if self.stopped:
                return 0
            logging.error('connecting error: %s' % str(ex))
            if reconnect:
                self.reconnect()
                return
            raise
 
        # add bot to the fleet
        #if not fleet.byname(self.name):
        #    fleet.addbot(self)
        self.connectlock.release()
        return res
 
    def shutdown(self):
 
        """ shutdown the bot. """
 
        logging.warn('irc - shutdown')
        self.stopoutputloop = 1
        self.stopped = 1
        time.sleep(1)
        self.tickqueue.put_nowait('go')
        self.close()
        self.connecting = False
        self.connected = False
        self.connectok.clear()
 
    def close(self):
 
        """ close the connection. """
 
        try:
            if self.cfg.has_key('ssl') and self.cfg['ssl']:
                self.oldsock.shutdown(2)
            else:
                self.sock.shutdown(2)
        except:
            pass
        try:
            if self.cfg.has_key('ssl') and self.cfg['ssl']:
                self.oldsock.close()
            else:
                self.sock.close()
            self.fsock.close()
        except:
            pass
 
    def exit(self):
 
        """ exit the bot. """
 
        self.stopreadloop = 1
        self.stopped = 1
        self.connected = 0
        self.shutdown()
 
    def reconnect(self):
 
        """ reconnect to the irc server. """
 
        try:
            if self.stopped:
                return 0
            # determine how many seconds to sleep
            if self.reconnectcount > 0:
                reconsleep = self.reconnectcount*15
                logging.warn('irc - sleeping %s seconds for reconnect' % reconsleep)
                time.sleep(reconsleep)
                if self.stopped:
                    logging.warn('irc - stopped.. not reconnecting')
                    return 1
                if self.connected:
                    logging.warn('irc - already connected .. not reconnecting')
                    return 1
            self.reconnectcount += 1
            self.exit()
            logging.warn('reconnecting')
            result = self.connect()
            return result
        except Exception, ex:
            handle_exception()
 
    def handle_pong(self, ievent):
 
        """ set pongcheck on received pong. """
 
        logging.debug('received server pong')
        self.pongcheck = 1
 
    def sendraw(self, txt):
 
        """ send raw text to the server. """
 
        if self.stopped:
            return
        logging.debug(u'irc - sending %s' % txt)
        self._raw(txt)
 
    def fakein(self, txt):
 
        """ do a fake ircevent. """
 
        if not txt:
            return
        logging.warn('irc - fakein - %s' % txt)
        self.handle_ievent(Ircevent().parse(self, txt))
 
    def say(self, printto, what, who=None, how='msg', fromm=None, speed=0, groupchat=False):
 
        """ say what to printto. """
 
        if not printto or not what or printto in self.nicks401:
            return
 
        # if who is set add "who: " to txt
        if not 'socket' in repr(printto):
            if who:
                what = "%s: %s" % (who, what)
            if speed > 9:
                speed = 9
            self.putonqueue(9-speed, (printto, what, who, how, fromm, speed))
            return
 
        # do the sending
        try:
            printto.send(what + '\n')
            time.sleep(0.001)
        except Exception, ex :
            if "Broken pipe" in str(ex) or "Bad file descriptor" in str(ex):
                return
            handle_exception()
 
    def out(self, printto, what, who=None, how='msg', fromm=None, speed=5):
 
        """ output the first 375 chars .. put the rest into cache. """
 
        # convert the data to the encoding
        try:
            what = toenc(what.rstrip())
        except Exception, ex:
            logging.error("can't output: %s" % str(ex))
            return
        if not what:
            return
 
        # split up in parts of 375 chars overflowing on word boundaries
        txtlist = splittxt(what)
        size = 0
 
        # send first block
        self.output(printto, txtlist[0], how, who, fromm)
 
        # see if we need to store output in less cache
        result = ""
        if len(txtlist) > 2:
            if not fromm:
                self.less(printto, txtlist[1:])
            else:
                self.less(fromm, txtlist[1:])
            size = len(txtlist) - 2
            result = txtlist[1:2][0]
            if size:
                result += " (+%s)" % size
        else:
            if len(txtlist) == 2:
                result = txtlist[1]
 
        # send second block
        if result:
            self.output(printto, result, how, who, fromm)
 
    def output(self, printto, what, how='msg' , who=None, fromm=None):
 
        """ first output .. then call saymonitor. """
 
        self.outputnolog(printto, what, how, who, fromm)
        saymonitor.put(self.name, printto, what, who, how, fromm)
 
    def outputnolog(self, printto, what, how, who=None, fromm=None):
 
        """ do output to irc server .. rate limit to 3 sec. """
 
        try:
            what = fix_format(what)
            if what:
                if how == 'msg':
                    self.privmsg(printto, what)
                elif how == 'notice':
                    self.notice(printto, what)
                elif how == 'ctcp':
                    self.ctcp(printto, what)
        except Exception, ex:
            handle_exception()
 
    def donick(self, nick, setorig=0, save=0, whois=0):
 
        """ change nick .. optionally set original nick and/or save to config.  """
 
        if not nick:
            return
 
        # disable auto 433 nick changing
        self.noauto433 = 1
 
        # set up wait for NICK command and issue NICK
        queue = Queue.Queue()
        nick = nick[:16]
        self.wait.register('NICK', self.nick[:16], queue, 12)
        self._raw('NICK %s\n' % nick)
        result = waitforqueue(queue, 5)
 
        # reenable 433 auto nick changing
        self.noauto433 = 0
        if not result:
            return 0
        self.nick = nick
 
        # send whois
        if whois:
            self.whois(nick)
 
        # set original
        if setorig:
            self.orignick = nick
 
        # save nick to state and config file
        if save:
            self.state['nick'] = nick
            self.state.save()
            self.cfg.set('nick', nick)
            self.cfg.save()
        return 1
 
    def join(self, channel, password=None):
 
        """ join channel with optional password. """
 
        if not channel:
            return
 
        # do join with password
        if password:
            self._raw('JOIN %s %s' % (channel, password))
            chan = ChannelBase(self.datadir + os.sep + 'channels' + os.sep + channel)
            if chan:
                chan.setpass('IRC', password)            
        else:
            # do pure join
            self._raw('JOIN %s' % channel)
 
        if self.state:
            if channel not in self.state.data.joinedchannels:
                self.state.data.joinedchannels.append(channel)
                self.state.save()
 
    def part(self, channel):
 
        """ leave channel. """
 
        if not channel:
            return
        self._raw('PART %s' % channel)
 
        try:
            self.state.data['joinedchannels'].remove(channel)
            self.state.save()
        except (KeyError, ValueError):
            pass
 
    def who(self, who):
 
        """ send who query. """
 
        if not who:
            return
        self.putonqueue(6, 'WHO %s' % who.strip())
 
    def names(self, channel):
 
        """ send names query. """
 
        if not channel:
            return
        self.putonqueue(6, 'NAMES %s' % channel)
 
    def whois(self, who):
 
        """ send whois query. """
 
        if not who:
            return
        self.putonqueue(6, 'WHOIS %s' % who)
 
    def privmsg(self, printto, what):
 
        """ send privmsg to irc server. """
 
        if not printto or not what:
            return
        self.send('PRIVMSG %s :%s' % (printto, what))
 
    def send(self, txt):
 
        """ send text to irc server. """
 
        if not txt:
            return
 
        if self.stopped:
            return
 
        try:
            self.outputlock.acquire()
            now = time.time()
            timetosleep = 4 - (now - self.lastoutput)
            if timetosleep > 0 and not self.nolimiter:
                logging.warn('irc - flood protect')
                time.sleep(timetosleep)
            txt = toenc(strippedtxt(txt))
            txt = txt.rstrip()
            self._raw(txt)
            try:
                self.outputlock.release()
            except:
                pass
        except Exception, ex:
            try:
                self.outputlock.release()
            except:
                pass
            if not self.blocking and 'broken pipe' in str(ex).lower():
                time.sleep(0.5)
            else:
                logging.error('irc - send error: %s' % str(ex))
                self.reconnect()
                return
 
    def voice(self, channel, who):
 
        """ give voice. """
 
        if not channel or not who:
            return
        self.putonqueue(9, 'MODE %s +v %s' % (channel, who))
 
    def doop(self, channel, who):
 
        """ give ops. """
 
        if not channel or not who:
            return
        self._raw('MODE %s +o %s' % (channel, who))
 
    def delop(self, channel, who):
 
        """ de-op user. """
 
        if not channel or not who:
            return
        self._raw('MODE %s -o %s' % (channel, who))
 
    def quit(self, reason='http://feedprovider.googlecode.com'):
 
        """ send quit message. """
 
        logging.debug('irc - sending quit')
        try:
            self._raw('QUIT :%s' % reason)
        except IOError:
            pass
 
    def notice(self, printto, what):
 
        """ send notice. """
 
        if not printto or not what:
            return
        self.send('NOTICE %s :%s' % (printto, what))
 
    def ctcp(self, printto, what):
 
        """ send ctcp privmsg. """
 
        if not printto or not what:
            return
        self.send("PRIVMSG %s :\001%s\001" % (printto, what))
 
    def ctcpreply(self, printto, what):
 
        """ send ctcp notice. """
 
        if not printto or not what:
            return
        self.putonqueue(2, "NOTICE %s :\001%s\001" % (printto, what))
 
    def action(self, printto, what):
 
        """ do action. """
 
        if not printto or not what:
            return
        self.putonqueue(9, "PRIVMSG %s :\001ACTION %s\001" % (printto, what))
 
    def handle_ievent(self, ievent):
 
        """ handle ircevent .. dispatch to 'handle_command' method. """ 
 
        try:
            if ievent.cmnd == 'JOIN' or ievent.msg:
                if ievent.nick in self.nicks401:
                    self.nicks401.remove(ievent.nick)
                    logging.warn('irc - %s joined .. unignoring' % ievent.nick)
            # see if the irc object has a method to handle the ievent
            method = getattr(self,'handle_' + ievent.cmnd.lower())
            # try to call method
            if method:
                try:
                    method(ievent)
                except:
                    handle_exception()
        except AttributeError:
            # no command method to handle event
            pass
        try:
            # see if there are wait callbacks
            self.wait.check(ievent)
        except:
            handle_exception()
 
    def handle_432(self, ievent):
 
        """ erroneous nick. """
 
        self.handle_433(ievent)
 
    def handle_433(self, ievent):
 
        """ handle nick already taken. """
 
        if self.noauto433:
            return
        nick = ievent.arguments[1]
        # check for alternick
        alternick = self.state['alternick']
        if alternick and not self.nickchanged:
            logging.warn('irc - using alternick %s' % alternick)
            self.donick(alternick)
            self.nickchanged = 1
            return
        # use random nick
        randomnick = getrandomnick()
        self._raw("NICK %s" % randomnick)
        self.nick = randomnick
        logging.warn('irc - ALERT: nick %s already in use/unavailable .. using randomnick %s' % (nick, randomnick))
        self.nickchanged = 1
 
    def handle_ping(self, ievent):
 
        """ send pong response. """
 
        if not ievent.txt:
            return
        self._raw('PONG :%s' % ievent.txt)
 
    def handle_001(self, ievent):
 
        """ we are connected.  """
 
        self.connectok.set()
        self.connected = True
        self.whois(self.nick)
 
    def handle_privmsg(self, ievent):
 
        """ check if msg is ctcp or not .. return 1 on handling. """
 
        if ievent.txt and ievent.txt[0] == '\001':
            self.handle_ctcp(ievent)
            return 1
 
    def handle_notice(self, ievent):
 
        """ handle notice event .. check for version request. """
 
        if ievent.txt and ievent.txt.find('VERSION') != -1:
            self.say(ievent.nick, self.cfg['version'], None, 'notice')
            return 1
 
    def handle_ctcp(self, ievent):
 
        """ handle client to client request .. version and ping. """
 
        if ievent.txt.find('VERSION') != -1:
            self.ctcpreply(ievent.nick, 'VERSION %s' % self.cfg['version'])
 
        if ievent.txt.find('PING') != -1:
            try:
                pingtime = ievent.txt.split()[1]
                pingtime2 = ievent.txt.split()[2]
                if pingtime:
                    self.ctcpreply(ievent.nick, 'PING ' + pingtime + ' ' + \
pingtime2)
            except IndexError:
                pass
 
    def handle_error(self, ievent):
 
        """ show error. """
        logging.error(str(ievent))
        if ievent.cmnd == "422":
            return
 
        if ievent.txt.startswith('Closing'):
            logging.error("irc - %s" % ievent.txt)
        else:
            logging.error("irc - %s - %s" % (ievent.arguments, ievent.txt))
 
    def ping(self):
 
        """ ping the irc server. """
 
        logging.debug('irc - sending ping')
        try:
            self.putonqueue(1, 'PING :%s' % self.server)
            return 1
        except Exception, ex:
            logging.debug("irc - can't send ping: %s" % str(ex))
            return 0
 
    def handle_401(self, ievent):
 
        """ handle 401 .. nick not available. """
 
        try:
            nick = ievent.arguments[1]
            if nick not in self.nicks401:
                logging.warn('irc - 401 on %s .. ignoring' % nick)
                self.nicks401.append(nick)
        except:
            pass
 
    def handle_700(self, ievent):
 
        """ handle 700 .. encoding request of the server. """
 
        try:
            self.encoding = ievent.arguments[1]
            logging.warn('irc - 700 encoding now is %s' % self.encoding)
        except:
            pass