diff options
Diffstat (limited to 'src/xmpppy-0.5.0rc1/doc/examples/commandsbot.py')
-rw-r--r-- | src/xmpppy-0.5.0rc1/doc/examples/commandsbot.py | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/src/xmpppy-0.5.0rc1/doc/examples/commandsbot.py b/src/xmpppy-0.5.0rc1/doc/examples/commandsbot.py new file mode 100644 index 00000000..f32c13c6 --- /dev/null +++ b/src/xmpppy-0.5.0rc1/doc/examples/commandsbot.py @@ -0,0 +1,289 @@ +#!/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() |