#!/usr/bin/python """ The example of using xmpppy's Ad-Hoc Commands (JEP-0050) implementation. """ import xmpp from xmpp.protocol import * options = { 'JID': 'circles@example.com', 'Password': '********', } class TestCommand(xmpp.commands.Command_Handler_Prototype): """ Example class. You should read source if you wish to understate how it works. This one actually does some calculations.""" name = 'testcommand' description = 'Circle calculations' def __init__(self, jid=''): """ Initialize some internals. Set the first request handler to self.calcTypeForm. """ xmpp.commands.Command_Handler_Prototype.__init__(self,jid) self.initial = { 'execute': self.initialForm } def initialForm(self, conn, request): """ Assign a session id and send the first form. """ sessionid = self.getSessionID() self.sessions[sessionid] = { 'jid':request.getFrom(), 'data':{'type':None} } # simulate that the client sent sessionid, so calcTypeForm will be able # to continue request.getTag(name="command").setAttr('sessionid', sessionid) return self.calcTypeForm(conn, request) def calcTypeForm(self, conn, request): """ Send first form to the requesting user. """ # get the session data sessionid = request.getTagAttr('command','sessionid') session = self.sessions[sessionid] # What to do when a user sends us a response? Note, that we should always # include 'execute', as it is a default action when requester does not send # exact action to do (should be set to the same as 'next' or 'complete' fields) session['actions'] = { 'cancel': self.cancel, 'next': self.calcTypeFormAccept, 'execute': self.calcTypeFormAccept, } # The form to send calctypefield = xmpp.DataField( name='calctype', desc='Calculation Type', value=session['data']['type'], options=[ ['Calculate the diameter of a circle','circlediameter'], ['Calculate the area of a circle','circlearea'] ], typ='list-single', required=1) # We set label attribute... seems that the xmpppy.DataField cannot do that calctypefield.setAttr('label', 'Calculation Type') form = xmpp.DataForm( title='Select type of operation', data=[ 'Use the combobox to select the type of calculation you would like'\ 'to do, then click Next.', calctypefield]) # Build a reply with the form reply = request.buildReply('result') replypayload = [ xmpp.Node('actions', attrs={'execute':'next'}, payload=[xmpp.Node('next')]), form] reply.addChild( name='command', namespace=NS_COMMANDS, attrs={ 'node':request.getTagAttr('command','node'), 'sessionid':sessionid, 'status':'executing'}, payload=replypayload) self._owner.send(reply) # Question: self._owner or conn? raise xmpp.NodeProcessed def calcTypeFormAccept(self, conn, request): """ Load the calcType form filled in by requester, then reply with the second form. """ # get the session data sessionid = request.getTagAttr('command','sessionid') session = self.sessions[sessionid] # load the form node = request.getTag(name='command').getTag(name='x',namespace=NS_DATA) form = xmpp.DataForm(node=node) # retrieve the data session['data']['type'] = form.getField('calctype').getValue() # send second form return self.calcDataForm(conn, request) def calcDataForm(self, conn, request, notavalue=None): """ Send a form asking for diameter. """ # get the session data sessionid = request.getTagAttr('command','sessionid') session = self.sessions[sessionid] # set the actions taken on requester's response session['actions'] = { 'cancel': self.cancel, 'prev': self.calcTypeForm, 'next': self.calcDataFormAccept, 'execute': self.calcDataFormAccept } # create a form radiusfield = xmpp.DataField(desc='Radius',name='radius',typ='text-single') radiusfield.setAttr('label', 'Radius') form = xmpp.DataForm( title = 'Enter the radius', data=[ 'Enter the radius of the circle (numbers only)', radiusfield]) # build a reply stanza reply = request.buildReply('result') replypayload = [ xmpp.Node('actions', attrs={'execute':'complete'}, payload=[xmpp.Node('complete'),xmpp.Node('prev')]), form] if notavalue: replypayload.append(xmpp.Node('note', attrs={'type': 'warn'}, payload=['You have to enter valid number.'])) reply.addChild( name='command', namespace=NS_COMMANDS, attrs={ 'node':request.getTagAttr('command','node'), 'sessionid':request.getTagAttr('command','sessionid'), 'status':'executing'}, payload=replypayload) self._owner.send(reply) raise xmpp.NodeProcessed def calcDataFormAccept(self, conn, request): """ Load the calcType form filled in by requester, then reply with the result. """ # get the session data sessionid = request.getTagAttr('command','sessionid') session = self.sessions[sessionid] # load the form node = request.getTag(name='command').getTag(name='x',namespace=NS_DATA) form = xmpp.DataForm(node=node) # retrieve the data; if the entered value is not a number, return to second stage try: value = float(form.getField('radius').getValue()) except: self.calcDataForm(conn, request, notavalue=True) # calculate the answer from math import pi if session['data']['type'] == 'circlearea': result = (value**2) * pi else: result = 2 * value * pi # build the result form form = xmpp.DataForm( typ='result', data=[xmpp.DataField(desc='result', name='result', value=result)]) # build the reply stanza reply = request.buildReply('result') reply.addChild( name='command', namespace=NS_COMMANDS, attrs={ 'node':request.getTagAttr('command','node'), 'sessionid':sessionid, 'status':'completed'}, payload=[form]) self._owner.send(reply) # erase the data about session del self.sessions[sessionid] raise xmpp.NodeProcessed def cancel(self, conn, request): """ Requester canceled the session, send a short reply. """ # get the session id sessionid = request.getTagAttr('command','sessionid') # send the reply reply = request.buildReply('result') reply.addChild( name='command', namespace=NS_COMMANDS, attrs={ 'node':request.getTagAttr('command','node'), 'sessionid':sessionid, 'status':'cancelled'}) self._owner.send(reply) # erase the data about session del self.sessions[sessionid] raise xmpp.NodeProcessed class ConnectionError: pass class AuthorizationError: pass class NotImplemented: pass class Bot: """ The main bot class. """ def __init__(self, JID, Password): """ Create a new bot. Connect to the server and log in. """ # connect... jid = xmpp.JID(JID) self.connection = xmpp.Client(jid.getDomain(), debug=['always', 'browser', 'testcommand']) result = self.connection.connect() if result is None: raise ConnectionError # authorize result = self.connection.auth(jid.getNode(), Password) if result is None: raise AuthorizationError # plugins # disco - needed by commands # warning: case of "plugin" method names are important! # to attach a command to Commands class, use .plugin() # to attach anything to Client class, use .PlugIn() self.disco = xmpp.browser.Browser() self.disco.PlugIn(self.connection) self.disco.setDiscoHandler({ 'info': { 'ids': [{ 'category': 'client', 'type': 'pc', 'name': 'Bot' }], 'features': [NS_DISCO_INFO], } }) self.commands = xmpp.commands.Commands(self.disco) self.commands.PlugIn(self.connection) self.command_test = TestCommand() self.command_test.plugin(self.commands) # presence self.connection.sendInitPresence(requestRoster=0) def loop(self): """ Do nothing except handling new xmpp stanzas. """ try: while self.connection.Process(1): pass except KeyboardInterrupt: pass bot = Bot(**options) bot.loop()