Thursday, October 16th, 2008

Palaver's present and future

Today, I made a new release of Palaver. It is an XMPP MUC component, created in python and twisted. The newest version is 0.6. This version has many bug fixes and speed improvements.

New features are memcache support for pgsql backend, and a memcache backend. The memcache backend is a more robust version of the in memory storage. All data is stored in memcached. This allows for palaver restarts without losing all of your data. It is not not as persistent as the other storage mechanisms but if you want a quick and fast MUC then it works, and is a bit better than the in memory dict storage.

I have also been adding MUC support to Wokkel and would like to use that protocol implementation for palaver.

The next release will be dependent on wokkel. So, palaver will then become just the storage mechanism code. Eventually, the wokkel code may make it to twisted words. I think this staging method will allow for quick use and testing. So, by the time it reaches twisted words it will have been used and tested well, beyond the required unit tests.

I am excited about the future of XMPP and Twisted words. The flexibility of twisted is gonna allow for a lot of cool components and servers!
(2 comments | Leave a comment)

Wednesday, October 8th, 2008

Twisted Wokkel Bot

A bot is not a new thing, it has been done in IRC for a long time. In IRC, a bot is a program that listens to messages in a channel and responds to them.

Bots are also being used in XMPP and particularly Multi-User Chat. You can see a weather bot in action at the following url :

http://speeqe.com/room/zzchschat@muc.appriss.com/

Using XMPP you can extend a bot to respond to messages that are not direct chats. XMPP is xml so you can send and respond to xml messages. This makes XMPP better than IRC.

At Chesspark bots are used extensively. There is one in particular used for a King of the Hill game. It combines MUC and PubSub XMPP extensions to achieve a fun game, where you knock your opponent off the hill by winning chess games.

There are many ways to create XMPP bots, but I have currently been working on MUC support for Wokkel. From Wokkel's web page : "Wokkel is collection of enhancements on top of the Twisted networking framework, written in Python. It mostly provides a testing ground for enhancements to the Jabber/XMPP protocol implementation as found in Twisted Words, that are meant to eventually move there. " Wokkel is nice for implementing and staging things before they can go into twisted.

Using the MUC client branch, I will show you how you can create a Jabber MUC bot. We have to use the branch because it is still under development and not in trunk yet. Hopefully it will be soon. Note that everything is not fully implemented but there is enough to make a bot.

This bot will be simple and just keep track of users coming in and out of a room. You will be able to see user activity using a last command.

First we check out the developement branch. Then we install it.


svn co https://svn.ik.nu/wokkel/branches/wokkel-muc-client-support-24 wokkel

python setup.py install



After this we will need to create the python class. Will will put it in the client.py file and call it MUCLastBot.

""" A simple last bot wokkel example """
from twisted.internet import defer
import datetime
from twisted.words.protocols.jabber import jid
from wokkel import muc

class MUCLastBot(muc.MUCClient):
   """ """



All this does it import the python modules we will be using and extend the muc.MUCClient class.

Next, we need to initialize and add 'last' support and methods to the bot.


    def __init__(self, server, room, nick):
        muc.MUCClient.__init__(self)
        self.server   = server
        self.room     = room
        self.nick     = nick
        self.room_jid = jid.internJID(self.room+'@'+self.server+'/'+self.nick)
        self.last     = {}
        self.activity = None

    def _getLast(self, nick):
        return self.last.get(nick.lower())
    
    def _setLast(self, user):
        user.last = datetime.datetime.now()
        self.last[user.nick.lower()] = user
        self.activity = user



This initializes the class with attributes we need and sets up methods to support user activity.


After this, we need to have the bot join a room. This is done with the 'initialized' method in MUCClient. This method is called after the XMPP client authenticates and its observers are initialized.


    def initialized(self):
        """The bot has connected to the xmpp server, now try to join the room.
        """
        self.join(self.server, self.room, self.nick).addCallback(self.initRoom)
        
    @defer.inlineCallbacks
    def initRoom(self, room):
        """Configure the room if we just created it.
        """

        if int(room.status) == muc.STATUS_CODE_CREATED:
            config_form = yield self.getConfigureForm(self.room_jid.userhost())
            
            # set config default
            config_result = yield self.configure(self.room_jid.userhost())
            


This code will make the bot join the room after start up and then will configure room if the status shows that the room was just created. It uses the 'join' method to join the room and when the bot joins the room 'initRoom' will be called.

You will notice the decorator 'inlineCallbacks', you can ignore this. This decorator allows the method to call deferred methods and use yield to get the call back result.

You can go to http://www.twistedmatrix.com to learn more. For our purposes we will just ignore it. :)

Next, we need to handle when someone joins and parts the room.


    def userJoinedRoom(self, room, user):
        """If a user joined a room, make sure they are in the last dict
        """
        self._setLast(user)

    def userLeftRoom(self, room, user):
        self._setLast(user)



All we do is mark their last activity.

We then need to handle commands and logging messages sent by users.


    def receivedGroupChat(self, room, user, body):
        # check if this message addresses the bot
        cmd       = None
        user_nick = None
        try:
            cmd, user_nick = body.split(" ")
        except ValueError:
            # value error means it was a one word body
            cmd = body
        cmd = cmd.replace("!", "")
        method = getattr(self, 'cmd_'+cmd, None)
        if method:
            method(room, user_nick)
        
        # log last message
        user.last_message = body
        self._setLast(user)




This method takes a groupchat message and processes it to see if there is a bot command. It also logs last message activity for the user.

So, the command we want to use is 'last', we need that method inorder to complete the bot.


   def cmd_last(self, room, user_nick):
        """
        """
        if user_nick is None:
            # show last person to do something
            self._sendLast(room, self.activity)
        else:
            u = self._getLast(user_nick)
            if u:
                self._sendLast(room, u)
            else:
                self.groupChat(self.room_jid, 'Sorry %s, That person is unknown to me.' % (user.nick,))

    def _sendLast(self, room, user):
        """ Grab last information from user and room and send it to the room.
        """
        last_message   = getattr(user,'last_message', '')
        last_stamp     = getattr(user,'last', '')
        
        if room.inRoster(user):
            message = """%s is in this room and said '%s' at %s.""" % (user.nick, last_message, last_stamp)
        else:
            message = """%s left this room at %s and last said '%s'.""" % (user.nick, last_stamp, last_message)

        self.groupChat(self.room_jid, message)


The last command checks the activity for the user given, or if no user is given it will then show the last active user. It will also tell you it does not know the user if it has never seen them in the room.

The '_sendLast' method sends that information back to the room as a groupchat.

The bot is now complete, we will need a conf file to start it up. To do this I will use a .tac file for twisted.


from twisted.application import service
from twisted.words.protocols.jabber import jid
from wokkel.client import XMPPClient

from client import MUCLastBot

application = service.Application("lastbot")

xmppclient = XMPPClient(jid.internJID("test@thetofu.com/lastbot"), "test")
xmppclient.logTraffic = True
mucbot = MUCLastBot('chat.speeqe.com','last', 'LastBot')
mucbot.setHandlerParent(xmppclient)
xmppclient.setServiceParent(application)



Simply use twistd to run the bot.


twisted -y muc.tac



That is it, have fun and look forward to more XMPP support in wokkel and twisted!

You can download muc.tac and client.py at the following url:

http://people.chesspark.com/~tofu/wokkel/lastbot/
(2 comments | Leave a comment)

Thursday, October 2nd, 2008

Speeqe about it.

One thing I do everyday is group chat via XMPP. There are lots of ways to do this, via many xmpp clients. If I am on my laptop or computer then having a client works out well. I can chat to my hearts content.

Often, a problem that comes about, is if I am away, and I need to communicate via a chat room, what do I do? I could use a remote computer and a command line XMPP client or I can use Speeqe.

Speeqe is a web based chat creator, you can create and theme your own chat rooms! There are many ways to use Speeqe for various different things, but the solution to my problem, I find very useful, is that you can connect to other chat rooms on the federated XMPP network.

To do this you can use the url trick, for example:

Jabber Development Room

http://speeqe.com/room/jdev@conference.jabber.org/

Charleston Linux Users Group

http://speeqe.com/room/csclug@conference.butterfat.net/

You can just start chatting anonymously or login via a XMPP account.

This trick, and the other things you can do with Speeqe, are cool, but best of all it does not end there. Speeqe is open source!

You can use and add features to Speeqe and the possibilities become endless. :)

The neat thing is it already uses open source software. It uses Strophe, Punjab, Palaver, Apache, and Django.

It can be set up to use any XMPP server and any Multi-User chat implementation. It is very flexible.

More examples of setting up and using Speeqe are sure to come!

Look forward to the official release of Speeqe!
(Leave a comment)