Make xmlstream use an asyncio loop
[slixmpp.git] / examples / adhoc_provider.py
blobc709feade0437118186d0be6b8f9442f8fe7ab65
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
4 """
5 Slixmpp: The Slick XMPP Library
6 Copyright (C) 2010 Nathanael C. Fritz
7 This file is part of Slixmpp.
9 See the file LICENSE for copying permission.
10 """
12 import sys
13 import logging
14 import getpass
15 from optparse import OptionParser
17 import slixmpp
19 # Python versions before 3.0 do not use UTF-8 encoding
20 # by default. To ensure that Unicode is handled properly
21 # throughout Slixmpp, we will set the default encoding
22 # ourselves to UTF-8.
23 if sys.version_info < (3, 0):
24 from slixmpp.util.misc_ops import setdefaultencoding
25 setdefaultencoding('utf8')
26 else:
27 raw_input = input
30 class CommandBot(slixmpp.ClientXMPP):
32 """
33 A simple Slixmpp bot that provides a basic
34 adhoc command.
35 """
37 def __init__(self, jid, password):
38 slixmpp.ClientXMPP.__init__(self, jid, password)
40 # The session_start event will be triggered when
41 # the bot establishes its connection with the server
42 # and the XML streams are ready for use. We want to
43 # listen for this event so that we we can initialize
44 # our roster.
45 self.add_event_handler("session_start", self.start)
47 def start(self, event):
48 """
49 Process the session_start event.
51 Typical actions for the session_start event are
52 requesting the roster and broadcasting an initial
53 presence stanza.
55 Arguments:
56 event -- An empty dictionary. The session_start
57 event does not provide any additional
58 data.
59 """
60 self.send_presence()
61 self.get_roster()
63 # We add the command after session_start has fired
64 # to ensure that the correct full JID is used.
66 # If using a component, may also pass jid keyword parameter.
68 self['xep_0050'].add_command(node='greeting',
69 name='Greeting',
70 handler=self._handle_command)
72 def _handle_command(self, iq, session):
73 """
74 Respond to the initial request for a command.
76 Arguments:
77 iq -- The iq stanza containing the command request.
78 session -- A dictionary of data relevant to the command
79 session. Additional, custom data may be saved
80 here to persist across handler callbacks.
81 """
82 form = self['xep_0004'].makeForm('form', 'Greeting')
83 form['instructions'] = 'Send a custom greeting to a JID'
84 form.addField(var='greeting',
85 ftype='text-single',
86 label='Your greeting')
88 session['payload'] = form
89 session['next'] = self._handle_command_complete
90 session['has_next'] = False
92 # Other useful session values:
93 # session['to'] -- The JID that received the
94 # command request.
95 # session['from'] -- The JID that sent the
96 # command request.
97 # session['has_next'] = True -- There are more steps to complete
98 # session['allow_complete'] = True -- Allow user to finish immediately
99 # and possibly skip steps
100 # session['cancel'] = handler -- Assign a handler for if the user
101 # cancels the command.
102 # session['notes'] = [ -- Add informative notes about the
103 # ('info', 'Info message'), command's results.
104 # ('warning', 'Warning message'),
105 # ('error', 'Error message')]
107 return session
109 def _handle_command_complete(self, payload, session):
111 Process a command result from the user.
113 Arguments:
114 payload -- Either a single item, such as a form, or a list
115 of items or forms if more than one form was
116 provided to the user. The payload may be any
117 stanza, such as jabber:x:oob for out of band
118 data, or jabber:x:data for typical data forms.
119 session -- A dictionary of data relevant to the command
120 session. Additional, custom data may be saved
121 here to persist across handler callbacks.
124 # In this case (as is typical), the payload is a form
125 form = payload
127 greeting = form['values']['greeting']
129 self.send_message(mto=session['from'],
130 mbody="%s, World!" % greeting,
131 mtype='chat')
133 # Having no return statement is the same as unsetting the 'payload'
134 # and 'next' session values and returning the session.
136 # Unless it is the final step, always return the session dictionary.
138 session['payload'] = None
139 session['next'] = None
141 return session
144 if __name__ == '__main__':
145 # Setup the command line arguments.
146 optp = OptionParser()
148 # Output verbosity options.
149 optp.add_option('-q', '--quiet', help='set logging to ERROR',
150 action='store_const', dest='loglevel',
151 const=logging.ERROR, default=logging.INFO)
152 optp.add_option('-d', '--debug', help='set logging to DEBUG',
153 action='store_const', dest='loglevel',
154 const=logging.DEBUG, default=logging.INFO)
155 optp.add_option('-v', '--verbose', help='set logging to COMM',
156 action='store_const', dest='loglevel',
157 const=5, default=logging.INFO)
159 # JID and password options.
160 optp.add_option("-j", "--jid", dest="jid",
161 help="JID to use")
162 optp.add_option("-p", "--password", dest="password",
163 help="password to use")
165 opts, args = optp.parse_args()
167 # Setup logging.
168 logging.basicConfig(level=opts.loglevel,
169 format='%(levelname)-8s %(message)s')
171 if opts.jid is None:
172 opts.jid = raw_input("Username: ")
173 if opts.password is None:
174 opts.password = getpass.getpass("Password: ")
176 # Setup the CommandBot and register plugins. Note that while plugins may
177 # have interdependencies, the order in which you register them does
178 # not matter.
179 xmpp = CommandBot(opts.jid, opts.password)
180 xmpp.register_plugin('xep_0030') # Service Discovery
181 xmpp.register_plugin('xep_0004') # Data Forms
182 xmpp.register_plugin('xep_0050') # Adhoc Commands
183 xmpp.register_plugin('xep_0199', {'keepalive': True, 'frequency':15})
185 # If you are working with an OpenFire server, you may need
186 # to adjust the SSL version used:
187 # xmpp.ssl_version = ssl.PROTOCOL_SSLv3
189 # If you want to verify the SSL certificates offered by a server:
190 # xmpp.ca_certs = "path/to/ca/cert"
192 # Connect to the XMPP server and start processing XMPP stanzas.
193 if xmpp.connect():
194 # If you do not have the dnspython library installed, you will need
195 # to manually specify the name of the server if it does not match
196 # the one in the JID. For example, to use Google Talk you would
197 # need to use:
199 # if xmpp.connect(('talk.google.com', 5222)):
200 # ...
201 xmpp.process(block=True)
202 print("Done")
203 else:
204 print("Unable to connect.")