====
Flow
====

__copyright__ = 'this file is in the public domain'

1) receive string from server
2) make string into an ircevent
3) basic handling of the ircevent
4) check if the event is a command to the bot, if so execute it
5) check to see if it triggers any callbacks

receive string and make it an ircevent
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

in gozerbot/irc.py:
::

    # read on socket .. make ircevent .. call the handle_ievent method
    res = self.fsock.readline()
    ievent = Ircevent().parse(self, res)
    self.handle_ievent(ievent)

basic handling of the ircevent
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

in gozerbot/bot.py:
::

    def handle_ievent(self, ievent):
        """ call Irc class method .. add to backlog .. check for callbacks """
        j = Ircevent()
        Irc.handle_ievent(self, ievent)
        self.backlog.append(ievent)
        j.copyin(ievent)
        callbacks.check(self, j)

in gozerbot/irc.py:
::

    def handle_ievent(self, ievent):
        """ handle ircevent .. dispatch to method """
        try:
            # see if the irc object has a method to handle the ievent
            method = getattr(self,'handle_' + ievent.cmnd.lower())
            # try to call method
            try:
                method(ievent)
            except:
                handle_exception()
        except AttributeError:
            pass
        # see if there are wait callbacks
        self.wait.check(ievent)

this will call a function handle_CMND on the bot.

command handling
~~~~~~~~~~~~~~~~

in gozerbot/bot.py:

    the handle_privmsg function handles the PRIVMSG commands and checks if a 
    command is given and if so calls plugins.trydispatch()

in gozerbot/plugins.py:

    the trydispatch function checks if the user should be ignored, if not
    checks for pipelining or multiple commands. in case of pipelining
    the ircevent queues entry will be chained. each command runs a dispatch
    function in its own thread.
    variables:  what is one of redispatcher of commands object, com is the 
    command triggered, bot is the bot on which the command is given and
    ievent is the ircevent that triggered the command
    ::
    
	def dispatch(self, what, com, bot, ievent):
    	    """ dispatch ievent """
    	    if bot.stopped:
        	return 0
    	    # check for user provided channel
    	    makeargrest(ievent)
    	    ievent.usercmnd = 1
    	    commandhandler.put(ievent.speed, what, com, bot, ievent)
    	    addievent(ievent.userhost, ievent)
    	    return 1

    dispatch pushes the triggered command to the commandhandler. this will
    allow use to do priority based scheduling of commands

in gozerbot/eventhandler.py:

    through the commandhandler the dispatch function of the redispatcher or
    commands object is called with command bot and ievent passed as argument

    what.dispatch(com, bot, ievent)

in gozerbot/redispatcher.py or gozerbot/commands.py:

    this is were the actual dispatch is taking place, the command or re 
    callback gets two arguments passed .. the bot and the ircevent
    ::
    
	def dispatch(self, com, bot, ievent):
    	    """ dispatch on ircevent """
    	    if bot.stopped:
        	return 0
    	    # execute command
    	    if com.threaded:
        	thr.start_bot_command(com.func, (bot, ievent))
    	    else:
        	try:
            	    com.func(bot, ievent)
        	except Exception, ex:
            	    handle_exception(ievent)
            	    return 0
    	    return 1

callback handling
~~~~~~~~~~~~~~~~~

in gozerbot/callbacks.py:

    this functions checks to see if any callback for an ircevent is
    registered and if so calls self.callback that will do the actual firing 
    of the callback.
    ::
    
	def check(self, bot, ievent):
    	    """ check for callbacks to be fired """
    	    # check for "ALL" callbacks   
    	    if self.cbs.has_key('ALL'):   
        	for cb in self.cbs['ALL']:
            	    self.callback(cb, bot, ievent)
    	    cmnd = ievent.cmnd.upper()   
    	    # check for CMND callbacks   
    	    if self.cbs.has_key(cmnd):   
        	for cb in self.cbs[cmnd]:
            	    self.callback(cb, bot, ievent)

    snippet of self.callback:
    
        every callback has a optional test function that is used to determine
        if the callback should fire or not .. cb.prereq. the actual callback
        is called in its own thread or in the main bot loop
	::
	
            # see if the callback pre requirement succeeds
            if cb.prereq:
                rlog(-10, 'callback', 'excecuting in loop %s' % str(cb.prereq))
                if not cb.prereq(bot, ievent):
                    return
            # check if callback function is there
            if not cb.func:
                return
            # start callback in its own thread
            rlog(-10, 'callback', 'excecuting callback %s' % str(cb.func))
            if cb.threaded:
                thr.start_new_thread(cb.func, (bot, ievent), cb.kwargs)
            else:
                cb.func(bot, ievent, **cb.kwargs)
