diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..88f9974 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +_build/* diff --git a/docs/_static/haiku.css b/docs/_static/haiku.css index 615ed47..9651d11 100644 --- a/docs/_static/haiku.css +++ b/docs/_static/haiku.css @@ -59,9 +59,10 @@ body { margin: auto; padding: 0px; font-family: "Helvetica Neueu", Helvetica, sans-serif; - min-width: 59em; + min-width: 30em; max-width: 70em; color: #444; + text-align: center; } div.footer { @@ -124,21 +125,25 @@ a.headerlink:hover { /* basic text elements */ div.content { + margin: auto; margin-top: 20px; - margin-left: 40px; - margin-right: 40px; margin-bottom: 50px; font-size: 0.9em; + width: 700px; + text-align: left; } /* heading and navigation */ div.header { position: relative; + margin: auto; margin-top: 125px; height: 85px; padding: 0 40px; font-family: "Yanone Kaffeesatz"; + text-align: left; + width: 750px; } div.header h1 { font-size: 2.6em; @@ -185,12 +190,12 @@ div.topnav { z-index: 0; } div.topnav p { + margin: auto; margin-top: 0; - margin-left: 40px; - margin-right: 40px; margin-bottom: 0px; text-align: right; font-size: 0.8em; + width: 750px; } div.bottomnav { background: #eeeeee; diff --git a/docs/_static/pygments.css b/docs/_static/pygments.css new file mode 100644 index 0000000..f04bc73 --- /dev/null +++ b/docs/_static/pygments.css @@ -0,0 +1,70 @@ +.highlight .hll { background-color: #ffffcc } +.highlight { background: #000000; color: #f6f3e8; } +.highlight .c { color: #7C7C7C; } /* Comment */ +.highlight .err { color: #f6f3e8; } /* Error */ +.highlight .g { color: #f6f3e8; } /* Generic */ +.highlight .k { color: #00ADEE; } /* Keyword */ +.highlight .l { color: #f6f3e8; } /* Literal */ +.highlight .n { color: #f6f3e8; } /* Name */ +.highlight .o { color: #f6f3e8; } /* Operator */ +.highlight .x { color: #f6f3e8; } /* Other */ +.highlight .p { color: #f6f3e8; } /* Punctuation */ +.highlight .cm { color: #7C7C7C; } /* Comment.Multiline */ +.highlight .cp { color: #96CBFE; } /* Comment.Preproc */ +.highlight .c1 { color: #7C7C7C; } /* Comment.Single */ +.highlight .cs { color: #7C7C7C; } /* Comment.Special */ +.highlight .gd { color: #f6f3e8; } /* Generic.Deleted */ +.highlight .ge { color: #f6f3e8; } /* Generic.Emph */ +.highlight .gr { color: #ffffff; background-color: #ff0000 } /* Generic.Error */ +.highlight .gh { color: #f6f3e8; font-weight: bold; } /* Generic.Heading */ +.highlight .gi { color: #f6f3e8; } /* Generic.Inserted */ +.highlight .go { color: #070707; } /* Generic.Output */ +.highlight .gp { color: #f6f3e8; } /* Generic.Prompt */ +.highlight .gs { color: #f6f3e8; } /* Generic.Strong */ +.highlight .gu { color: #f6f3e8; font-weight: bold; } /* Generic.Subheading */ +.highlight .gt { color: #ffffff; font-weight: bold; background-color: #FF6C60 } /* Generic.Traceback */ +.highlight .kc { color: #6699CC; } /* Keyword.Constant */ +.highlight .kd { color: #6699CC; } /* Keyword.Declaration */ +.highlight .kn { color: #6699CC; } /* Keyword.Namespace */ +.highlight .kp { color: #6699CC; } /* Keyword.Pseudo */ +.highlight .kr { color: #6699CC; } /* Keyword.Reserved */ +.highlight .kt { color: #FFFFB6; } /* Keyword.Type */ +.highlight .ld { color: #f6f3e8; } /* Literal.Date */ +.highlight .m { color: #FF73FD; } /* Literal.Number */ +.highlight .s { color: #F46DBA;/*#A8FF60;*/ } /* Literal.String */ +.highlight .na { color: #f6f3e8; } /* Name.Attribute */ +.highlight .nb { color: #f6f3e8; } /* Name.Builtin */ +.highlight .nc { color: #f6f3e8; } /* Name.Class */ +.highlight .no { color: #99CC99; } /* Name.Constant */ +.highlight .nd { color: #f6f3e8; } /* Name.Decorator */ +.highlight .ni { color: #E18964; } /* Name.Entity */ +.highlight .ne { color: #f6f3e8; } /* Name.Exception */ +.highlight .nf { color: #F64DBA; } /* Name.Function */ +.highlight .nl { color: #f6f3e8; } /* Name.Label */ +.highlight .nn { color: #f6f3e8; } /* Name.Namespace */ +.highlight .nx { color: #f6f3e8; } /* Name.Other */ +.highlight .py { color: #f6f3e8; } /* Name.Property */ +.highlight .nt { color: #00ADEE; } /* Name.Tag */ +.highlight .nv { color: #C6C5FE; } /* Name.Variable */ +.highlight .ow { color: #ffffff; } /* Operator.Word */ +.highlight .w { color: #f6f3e8; } /* Text.Whitespace */ +.highlight .mf { color: #FF73FD; } /* Literal.Number.Float */ +.highlight .mh { color: #FF73FD; } /* Literal.Number.Hex */ +.highlight .mi { color: #FF73FD; } /* Literal.Number.Integer */ +.highlight .mo { color: #FF73FD; } /* Literal.Number.Oct */ +.highlight .sb { color: #A8FF60; } /* Literal.String.Backtick */ +.highlight .sc { color: #A8FF60; } /* Literal.String.Char */ +.highlight .sd { color: #A8FF60; } /* Literal.String.Doc */ +.highlight .s2 { color: #A8FF60; } /* Literal.String.Double */ +.highlight .se { color: #A8FF60; } /* Literal.String.Escape */ +.highlight .sh { color: #A8FF60; } /* Literal.String.Heredoc */ +.highlight .si { color: #A8FF60; } /* Literal.String.Interpol */ +.highlight .sx { color: #A8FF60; } /* Literal.String.Other */ +.highlight .sr { color: #A8FF60; } /* Literal.String.Regex */ +.highlight .s1 { color: #A8FF60; } /* Literal.String.Single */ +.highlight .ss { color: #A8FF60; } /* Literal.String.Symbol */ +.highlight .bp { color: #f6f3e8; } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #C6C5FE; } /* Name.Variable.Class */ +.highlight .vg { color: #C6C5FE; } /* Name.Variable.Global */ +.highlight .vi { color: #C6C5FE; } /* Name.Variable.Instance */ +.highlight .il { color: #FF73FD; } /* Literal.Number.Integer.Long */ diff --git a/docs/conf.py b/docs/conf.py index dd83f24..72e39d0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,7 +50,7 @@ copyright = u'2011, Nathan Fritz, Lance Stout' # The short X.Y version. version = '1.0' # The full version, including alpha/beta/rc tags. -release = '1.0RC3' +release = '1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -91,7 +91,7 @@ pygments_style = 'tango' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'nature' +html_theme = 'haiku' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/docs/index.rst b/docs/index.rst index fc6541d..dc482d6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,7 +13,7 @@ SleekXMPP ``develop`` branch. **Latest Stable Release** - - `1.0 RC3 `_ + - `1.0 `_ **Develop Releases** - `Latest Develop Version `_ diff --git a/examples/adhoc_provider.py b/examples/adhoc_provider.py new file mode 100755 index 0000000..a72158c --- /dev/null +++ b/examples/adhoc_provider.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import sys +import logging +import getpass +from optparse import OptionParser + +import sleekxmpp + +# Python versions before 3.0 do not use UTF-8 encoding +# by default. To ensure that Unicode is handled properly +# throughout SleekXMPP, we will set the default encoding +# ourselves to UTF-8. +if sys.version_info < (3, 0): + reload(sys) + sys.setdefaultencoding('utf8') +else: + raw_input = input + + +class CommandBot(sleekxmpp.ClientXMPP): + + """ + A simple SleekXMPP bot that provides a basic + adhoc command. + """ + + def __init__(self, jid, password): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + + # The session_start event will be triggered when + # the bot establishes its connection with the server + # and the XML streams are ready for use. We want to + # listen for this event so that we we can initialize + # our roster. + self.add_event_handler("session_start", self.start) + + def start(self, event): + """ + Process the session_start event. + + Typical actions for the session_start event are + requesting the roster and broadcasting an initial + presence stanza. + + Arguments: + event -- An empty dictionary. The session_start + event does not provide any additional + data. + """ + self.send_presence() + self.get_roster() + + # We add the command after session_start has fired + # to ensure that the correct full JID is used. + + # If using a component, may also pass jid keyword parameter. + + self['xep_0050'].add_command(node='greeting', + name='Greeting', + handler=self._handle_command) + + def _handle_command(self, iq, session): + """ + Respond to the initial request for a command. + + Arguments: + iq -- The iq stanza containing the command request. + session -- A dictionary of data relevant to the command + session. Additional, custom data may be saved + here to persist across handler callbacks. + """ + form = self['xep_0004'].makeForm('form', 'Greeting') + form['instructions'] = 'Send a custom greeting to a JID' + form.addField(var='greeting', + ftype='text-single', + label='Your greeting') + + session['payload'] = form + session['next'] = self._handle_command_complete + session['has_next'] = False + + # Other useful session values: + # session['to'] -- The JID that received the + # command request. + # session['from'] -- The JID that sent the + # command request. + # session['has_next'] = True -- There are more steps to complete + # session['allow_complete'] = True -- Allow user to finish immediately + # and possibly skip steps + # session['cancel'] = handler -- Assign a handler for if the user + # cancels the command. + # session['notes'] = [ -- Add informative notes about the + # ('info', 'Info message'), command's results. + # ('warning', 'Warning message'), + # ('error', 'Error message')] + + return session + + def _handle_command_complete(self, payload, session): + """ + Process a command result from the user. + + Arguments: + payload -- Either a single item, such as a form, or a list + of items or forms if more than one form was + provided to the user. The payload may be any + stanza, such as jabber:x:oob for out of band + data, or jabber:x:data for typical data forms. + session -- A dictionary of data relevant to the command + session. Additional, custom data may be saved + here to persist across handler callbacks. + """ + + # In this case (as is typical), the payload is a form + form = payload + + greeting = form['values']['greeting'] + + self.send_message(mto=session['from'], + mbody="%s, World!" % greeting, + mtype='chat') + + # Having no return statement is the same as unsetting the 'payload' + # and 'next' session values and returning the session. + + # Unless it is the final step, always return the session dictionary. + + session['payload'] = None + session['next'] = None + + return session + + +if __name__ == '__main__': + # Setup the command line arguments. + optp = OptionParser() + + # Output verbosity options. + optp.add_option('-q', '--quiet', help='set logging to ERROR', + action='store_const', dest='loglevel', + const=logging.ERROR, default=logging.INFO) + optp.add_option('-d', '--debug', help='set logging to DEBUG', + action='store_const', dest='loglevel', + const=logging.DEBUG, default=logging.INFO) + optp.add_option('-v', '--verbose', help='set logging to COMM', + action='store_const', dest='loglevel', + const=5, default=logging.INFO) + + # JID and password options. + optp.add_option("-j", "--jid", dest="jid", + help="JID to use") + optp.add_option("-p", "--password", dest="password", + help="password to use") + + opts, args = optp.parse_args() + + # Setup logging. + logging.basicConfig(level=opts.loglevel, + format='%(levelname)-8s %(message)s') + + if opts.jid is None: + opts.jid = raw_input("Username: ") + if opts.password is None: + opts.password = getpass.getpass("Password: ") + + # Setup the CommandBot and register plugins. Note that while plugins may + # have interdependencies, the order in which you register them does + # not matter. + xmpp = CommandBot(opts.jid, opts.password) + xmpp.register_plugin('xep_0030') # Service Discovery + xmpp.register_plugin('xep_0004') # Data Forms + xmpp.register_plugin('xep_0050') # Adhoc Commands + xmpp.register_plugin('xep_0199', {'keepalive': True, 'frequency':15}) + + # If you are working with an OpenFire server, you may need + # to adjust the SSL version used: + # xmpp.ssl_version = ssl.PROTOCOL_SSLv3 + + # If you want to verify the SSL certificates offered by a server: + # xmpp.ca_certs = "path/to/ca/cert" + + # Connect to the XMPP server and start processing XMPP stanzas. + if xmpp.connect(): + # If you do not have the dnspython library installed, you will need + # to manually specify the name of the server if it does not match + # the one in the JID. For example, to use Google Talk you would + # need to use: + # + # if xmpp.connect(('talk.google.com', 5222)): + # ... + xmpp.process(block=True) + print("Done") + else: + print("Unable to connect.") diff --git a/examples/adhoc_user.py b/examples/adhoc_user.py new file mode 100755 index 0000000..bbd42d8 --- /dev/null +++ b/examples/adhoc_user.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import sys +import logging +import getpass +from optparse import OptionParser + +import sleekxmpp + +# Python versions before 3.0 do not use UTF-8 encoding +# by default. To ensure that Unicode is handled properly +# throughout SleekXMPP, we will set the default encoding +# ourselves to UTF-8. +if sys.version_info < (3, 0): + reload(sys) + sys.setdefaultencoding('utf8') +else: + raw_input = input + + +class CommandUserBot(sleekxmpp.ClientXMPP): + + """ + A simple SleekXMPP bot that uses the adhoc command + provided by the adhoc_provider.py example. + """ + + def __init__(self, jid, password, other, greeting): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + + self.command_provider = other + self.greeting = greeting + + # The session_start event will be triggered when + # the bot establishes its connection with the server + # and the XML streams are ready for use. We want to + # listen for this event so that we we can initialize + # our roster. + self.add_event_handler("session_start", self.start) + self.add_event_handler("message", self.message) + + def start(self, event): + """ + Process the session_start event. + + Typical actions for the session_start event are + requesting the roster and broadcasting an initial + presence stanza. + + Arguments: + event -- An empty dictionary. The session_start + event does not provide any additional + data. + """ + self.send_presence() + self.get_roster() + + # We first create a session dictionary containing: + # 'next' -- the handler to execute on a successful response + # 'error' -- the handler to execute if an error occurs + + # The session may also contain custom data. + + session = {'greeting': self.greeting, + 'next': self._command_start, + 'error': self._command_error} + + self['xep_0050'].start_command(jid=self.command_provider, + node='greeting', + session=session) + + def message(self, msg): + """ + Process incoming message stanzas. + + Arguments: + msg -- The received message stanza. + """ + logging.info(msg['body']) + + def _command_start(self, iq, session): + """ + Process the initial command result. + + Arguments: + iq -- The iq stanza containing the command result. + session -- A dictionary of data relevant to the command + session. Additional, custom data may be saved + here to persist across handler callbacks. + """ + + # The greeting command provides a form with a single field: + # + # + # + + form = self['xep_0004'].makeForm(ftype='submit') + form.addField(var='greeting', + value=session['greeting']) + + session['payload'] = form + + # We don't need to process the next result. + session['next'] = None + + # Other options include using: + # continue_command() -- Continue to the next step in the workflow + # cancel_command() -- Stop command execution. + + self['xep_0050'].complete_command(session) + + def _command_error(self, iq, session): + """ + Process an error that occurs during command execution. + + Arguments: + iq -- The iq stanza containing the error. + session -- A dictionary of data relevant to the command + session. Additional, custom data may be saved + here to persist across handler callbacks. + """ + logging.error("COMMAND: %s %s" % (iq['error']['condition'], + iq['error']['text'])) + + # Terminate the command's execution and clear its session. + # The session will automatically be cleared if no error + # handler is provided. + self['xep_0050'].terminate_command(session) + self.disconnect() + + +if __name__ == '__main__': + # Setup the command line arguments. + optp = OptionParser() + + # Output verbosity options. + optp.add_option('-q', '--quiet', help='set logging to ERROR', + action='store_const', dest='loglevel', + const=logging.ERROR, default=logging.INFO) + optp.add_option('-d', '--debug', help='set logging to DEBUG', + action='store_const', dest='loglevel', + const=logging.DEBUG, default=logging.INFO) + optp.add_option('-v', '--verbose', help='set logging to COMM', + action='store_const', dest='loglevel', + const=5, default=logging.INFO) + + # JID and password options. + optp.add_option("-j", "--jid", dest="jid", + help="JID to use") + optp.add_option("-p", "--password", dest="password", + help="password to use") + optp.add_option("-o", "--other", dest="other", + help="JID providing commands") + optp.add_option("-g", "--greeting", dest="greeting", + help="Greeting") + + opts, args = optp.parse_args() + + # Setup logging. + logging.basicConfig(level=opts.loglevel, + format='%(levelname)-8s %(message)s') + + if opts.jid is None: + opts.jid = raw_input("Username: ") + if opts.password is None: + opts.password = getpass.getpass("Password: ") + if opts.other is None: + opts.other = raw_input("JID Providing Commands: ") + if opts.greeting is None: + opts.greeting = raw_input("Greeting: ") + + # Setup the CommandBot and register plugins. Note that while plugins may + # have interdependencies, the order in which you register them does + # not matter. + xmpp = CommandUserBot(opts.jid, opts.password, opts.other, opts.greeting) + xmpp.register_plugin('xep_0030') # Service Discovery + xmpp.register_plugin('xep_0004') # Data Forms + xmpp.register_plugin('xep_0050') # Adhoc Commands + + # If you are working with an OpenFire server, you may need + # to adjust the SSL version used: + # xmpp.ssl_version = ssl.PROTOCOL_SSLv3 + + # If you want to verify the SSL certificates offered by a server: + # xmpp.ca_certs = "path/to/ca/cert" + + # Connect to the XMPP server and start processing XMPP stanzas. + if xmpp.connect(): + # If you do not have the dnspython library installed, you will need + # to manually specify the name of the server if it does not match + # the one in the JID. For example, to use Google Talk you would + # need to use: + # + # if xmpp.connect(('talk.google.com', 5222)): + # ... + xmpp.process(block=True) + print("Done") + else: + print("Unable to connect.") diff --git a/examples/disco_browser.py b/examples/disco_browser.py new file mode 100755 index 0000000..0d282d7 --- /dev/null +++ b/examples/disco_browser.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import sys +import logging +import getpass +from optparse import OptionParser + +import sleekxmpp + + +# Python versions before 3.0 do not use UTF-8 encoding +# by default. To ensure that Unicode is handled properly +# throughout SleekXMPP, we will set the default encoding +# ourselves to UTF-8. +if sys.version_info < (3, 0): + reload(sys) + sys.setdefaultencoding('utf8') +else: + raw_input = input + + +class Disco(sleekxmpp.ClientXMPP): + + """ + A demonstration for using basic service discovery. + + Send a disco#info and disco#items request to a JID/node combination, + and print out the results. + + May also request only particular info categories such as just features, + or just items. + """ + + def __init__(self, jid, password, target_jid, target_node='', get=''): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + + # Using service discovery requires the XEP-0030 plugin. + self.register_plugin('xep_0030') + + self.get = get + self.target_jid = target_jid + self.target_node = target_node + + # Values to control which disco entities are reported + self.info_types = ['', 'all', 'info', 'identities', 'features'] + self.identity_types = ['', 'all', 'info', 'identities'] + self.feature_types = ['', 'all', 'info', 'features'] + self.items_types = ['', 'all', 'items'] + + + # The session_start event will be triggered when + # the bot establishes its connection with the server + # and the XML streams are ready for use. We want to + # listen for this event so that we we can initialize + # our roster. + self.add_event_handler("session_start", self.start) + + def start(self, event): + """ + Process the session_start event. + + Typical actions for the session_start event are + requesting the roster and broadcasting an initial + presence stanza. + + In this case, we send disco#info and disco#items + stanzas to the requested JID and print the results. + + Arguments: + event -- An empty dictionary. The session_start + event does not provide any additional + data. + """ + self.get_roster() + self.send_presence() + + if self.get in self.info_types: + # By using block=True, the result stanza will be + # returned. Execution will block until the reply is + # received. Non-blocking options would be to listen + # for the disco_info event, or passing a handler + # function using the callback parameter. + info = self['xep_0030'].get_info(jid=self.target_jid, + node=self.target_node, + block=True) + if self.get in self.items_types: + # The same applies from above. Listen for the + # disco_items event or pass a callback function + # if you need to process a non-blocking request. + items = self['xep_0030'].get_items(jid=self.target_jid, + node=self.target_node, + block=True) + else: + logging.error("Invalid disco request type.") + self.disconnect() + return + + header = 'XMPP Service Discovery: %s' % self.target_jid + print(header) + print('-' * len(header)) + if self.target_node != '': + print('Node: %s' % self.target_node) + print('-' * len(header)) + + if self.get in self.identity_types: + print('Identities:') + for identity in info['disco_info']['identities']: + print(' - %s' % str(identity)) + + if self.get in self.feature_types: + print('Features:') + for feature in info['disco_info']['features']: + print(' - %s' % feature) + + if self.get in self.items_types: + print('Items:') + for item in items['disco_items']['items']: + print(' - %s' % str(item)) + + self.disconnect() + + +if __name__ == '__main__': + # Setup the command line arguments. + optp = OptionParser() + optp.version = '%%prog 0.1' + optp.usage = "Usage: %%prog [options] %s []" % \ + 'all|info|items|identities|features' + + optp.add_option('-q','--quiet', help='set logging to ERROR', + action='store_const', + dest='loglevel', + const=logging.ERROR, + default=logging.ERROR) + optp.add_option('-d','--debug', help='set logging to DEBUG', + action='store_const', + dest='loglevel', + const=logging.DEBUG, + default=logging.ERROR) + optp.add_option('-v','--verbose', help='set logging to COMM', + action='store_const', + dest='loglevel', + const=5, + default=logging.ERROR) + + # JID and password options. + optp.add_option("-j", "--jid", dest="jid", + help="JID to use") + optp.add_option("-p", "--password", dest="password", + help="password to use") + opts,args = optp.parse_args() + + # Setup logging. + logging.basicConfig(level=opts.loglevel, + format='%(levelname)-8s %(message)s') + + if len(args) < 2: + optp.print_help() + exit() + + if len(args) == 2: + args = (args[0], args[1], '') + + if opts.jid is None: + opts.jid = raw_input("Username: ") + if opts.password is None: + opts.password = getpass.getpass("Password: ") + + # Setup the Disco browser. + xmpp = Disco(opts.jid, opts.password, args[1], args[2], args[0]) + + # If you are working with an OpenFire server, you may need + # to adjust the SSL version used: + # xmpp.ssl_version = ssl.PROTOCOL_SSLv3 + + # If you want to verify the SSL certificates offered by a server: + # xmpp.ca_certs = "path/to/ca/cert" + + # Connect to the XMPP server and start processing XMPP stanzas. + if xmpp.connect(): + # If you do not have the dnspython library installed, you will need + # to manually specify the name of the server if it does not match + # the one in the JID. For example, to use Google Talk you would + # need to use: + # + # if xmpp.connect(('talk.google.com', 5222)): + # ... + xmpp.process(block=True) + else: + print("Unable to connect.") diff --git a/examples/echo_client.py b/examples/echo_client.py new file mode 100755 index 0000000..7e553c4 --- /dev/null +++ b/examples/echo_client.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import sys +import logging +import getpass +from optparse import OptionParser + +import sleekxmpp + +# Python versions before 3.0 do not use UTF-8 encoding +# by default. To ensure that Unicode is handled properly +# throughout SleekXMPP, we will set the default encoding +# ourselves to UTF-8. +if sys.version_info < (3, 0): + reload(sys) + sys.setdefaultencoding('utf8') +else: + raw_input = input + + +class EchoBot(sleekxmpp.ClientXMPP): + + """ + A simple SleekXMPP bot that will echo messages it + receives, along with a short thank you message. + """ + + def __init__(self, jid, password): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + + # The session_start event will be triggered when + # the bot establishes its connection with the server + # and the XML streams are ready for use. We want to + # listen for this event so that we we can initialize + # our roster. + self.add_event_handler("session_start", self.start) + + # The message event is triggered whenever a message + # stanza is received. Be aware that that includes + # MUC messages and error messages. + self.add_event_handler("message", self.message) + + def start(self, event): + """ + Process the session_start event. + + Typical actions for the session_start event are + requesting the roster and broadcasting an initial + presence stanza. + + Arguments: + event -- An empty dictionary. The session_start + event does not provide any additional + data. + """ + self.send_presence() + self.get_roster() + + def message(self, msg): + """ + Process incoming message stanzas. Be aware that this also + includes MUC messages and error messages. It is usually + a good idea to check the messages's type before processing + or sending replies. + + Arguments: + msg -- The received message stanza. See the documentation + for stanza objects and the Message stanza to see + how it may be used. + """ + if msg['type'] in ('chat', 'normal'): + msg.reply("Thanks for sending\n%(body)s" % msg).send() + + +if __name__ == '__main__': + # Setup the command line arguments. + optp = OptionParser() + + # Output verbosity options. + optp.add_option('-q', '--quiet', help='set logging to ERROR', + action='store_const', dest='loglevel', + const=logging.ERROR, default=logging.INFO) + optp.add_option('-d', '--debug', help='set logging to DEBUG', + action='store_const', dest='loglevel', + const=logging.DEBUG, default=logging.INFO) + optp.add_option('-v', '--verbose', help='set logging to COMM', + action='store_const', dest='loglevel', + const=5, default=logging.INFO) + + # JID and password options. + optp.add_option("-j", "--jid", dest="jid", + help="JID to use") + optp.add_option("-p", "--password", dest="password", + help="password to use") + + opts, args = optp.parse_args() + + # Setup logging. + logging.basicConfig(level=opts.loglevel, + format='%(levelname)-8s %(message)s') + + if opts.jid is None: + opts.jid = raw_input("Username: ") + if opts.password is None: + opts.password = getpass.getpass("Password: ") + + # Setup the EchoBot and register plugins. Note that while plugins may + # have interdependencies, the order in which you register them does + # not matter. + xmpp = EchoBot(opts.jid, opts.password) + xmpp.register_plugin('xep_0030') # Service Discovery + xmpp.register_plugin('xep_0004') # Data Forms + xmpp.register_plugin('xep_0060') # PubSub + xmpp.register_plugin('xep_0199') # XMPP Ping + + # If you are working with an OpenFire server, you may need + # to adjust the SSL version used: + # xmpp.ssl_version = ssl.PROTOCOL_SSLv3 + + # If you want to verify the SSL certificates offered by a server: + # xmpp.ca_certs = "path/to/ca/cert" + + # Connect to the XMPP server and start processing XMPP stanzas. + if xmpp.connect(): + # If you do not have the dnspython library installed, you will need + # to manually specify the name of the server if it does not match + # the one in the JID. For example, to use Google Talk you would + # need to use: + # + # if xmpp.connect(('talk.google.com', 5222)): + # ... + xmpp.process(block=True) + print("Done") + else: + print("Unable to connect.") diff --git a/examples/echo_component.py b/examples/echo_component.py new file mode 100755 index 0000000..82f6eb9 --- /dev/null +++ b/examples/echo_component.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import sys +import logging +import getpass +from optparse import OptionParser + +import sleekxmpp +from sleekxmpp.componentxmpp import ComponentXMPP + +# Python versions before 3.0 do not use UTF-8 encoding +# by default. To ensure that Unicode is handled properly +# throughout SleekXMPP, we will set the default encoding +# ourselves to UTF-8. +if sys.version_info < (3, 0): + reload(sys) + sys.setdefaultencoding('utf8') +else: + raw_input = input + + +class EchoComponent(ComponentXMPP): + + """ + A simple SleekXMPP component that echoes messages. + """ + + def __init__(self, jid, secret, server, port): + ComponentXMPP.__init__(self, jid, secret, server, port) + + # You don't need a session_start handler, but that is + # where you would broadcast initial presence. + + # The message event is triggered whenever a message + # stanza is received. Be aware that that includes + # MUC messages and error messages. + self.add_event_handler("message", self.message) + + def message(self, msg): + """ + Process incoming message stanzas. Be aware that this also + includes MUC messages and error messages. It is usually + a good idea to check the messages's type before processing + or sending replies. + + Since a component may send messages from any number of JIDs, + it is best to always include a from JID. + + Arguments: + msg -- The received message stanza. See the documentation + for stanza objects and the Message stanza to see + how it may be used. + """ + # The reply method will use the messages 'to' JID as the + # outgoing reply's 'from' JID. + msg.reply("Thanks for sending\n%(body)s" % msg).send() + + +if __name__ == '__main__': + # Setup the command line arguments. + optp = OptionParser() + + # Output verbosity options. + optp.add_option('-q', '--quiet', help='set logging to ERROR', + action='store_const', dest='loglevel', + const=logging.ERROR, default=logging.INFO) + optp.add_option('-d', '--debug', help='set logging to DEBUG', + action='store_const', dest='loglevel', + const=logging.DEBUG, default=logging.INFO) + optp.add_option('-v', '--verbose', help='set logging to COMM', + action='store_const', dest='loglevel', + const=5, default=logging.INFO) + + # JID and password options. + optp.add_option("-j", "--jid", dest="jid", + help="JID to use") + optp.add_option("-p", "--password", dest="password", + help="password to use") + optp.add_option("-s", "--server", dest="server", + help="server to connect to") + optp.add_option("-P", "--port", dest="port", + help="port to connect to") + + opts, args = optp.parse_args() + + if opts.jid is None: + opts.jid = raw_input("Component JID: ") + if opts.password is None: + opts.password = getpass.getpass("Password: ") + if opts.server is None: + opts.server = raw_input("Server: ") + if opts.port is None: + opts.port = int(raw_input("Port: ")) + + # Setup logging. + logging.basicConfig(level=opts.loglevel, + format='%(levelname)-8s %(message)s') + + # Setup the EchoComponent and register plugins. Note that while plugins + # may have interdependencies, the order in which you register them does + # not matter. + xmpp = EchoComponent(opts.jid, opts.password, opts.server, opts.port) + xmpp.registerPlugin('xep_0030') # Service Discovery + xmpp.registerPlugin('xep_0004') # Data Forms + xmpp.registerPlugin('xep_0060') # PubSub + xmpp.registerPlugin('xep_0199') # XMPP Ping + + # Connect to the XMPP server and start processing XMPP stanzas. + if xmpp.connect(): + xmpp.process(block=True) + print("Done") + else: + print("Unable to connect.") diff --git a/examples/muc.py b/examples/muc.py new file mode 100755 index 0000000..8fe2eb4 --- /dev/null +++ b/examples/muc.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import sys +import logging +import getpass +from optparse import OptionParser + +import sleekxmpp + +# Python versions before 3.0 do not use UTF-8 encoding +# by default. To ensure that Unicode is handled properly +# throughout SleekXMPP, we will set the default encoding +# ourselves to UTF-8. +if sys.version_info < (3, 0): + reload(sys) + sys.setdefaultencoding('utf8') +else: + raw_input = input + + +class MUCBot(sleekxmpp.ClientXMPP): + + """ + A simple SleekXMPP bot that will greets those + who enter the room, and acknowledge any messages + that mentions the bot's nickname. + """ + + def __init__(self, jid, password, room, nick): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + + self.room = room + self.nick = nick + + # The session_start event will be triggered when + # the bot establishes its connection with the server + # and the XML streams are ready for use. We want to + # listen for this event so that we we can initialize + # our roster. + self.add_event_handler("session_start", self.start) + + # The groupchat_message event is triggered whenever a message + # stanza is received from any chat room. If you also also + # register a handler for the 'message' event, MUC messages + # will be processed by both handlers. + self.add_event_handler("groupchat_message", self.muc_message) + + # The groupchat_presence event is triggered whenever a + # presence stanza is received from any chat room, including + # any presences you send yourself. To limit event handling + # to a single room, use the events muc::room@server::presence, + # muc::room@server::got_online, or muc::room@server::got_offline. + self.add_event_handler("muc::%s::got_online" % self.room, + self.muc_online) + + + def start(self, event): + """ + Process the session_start event. + + Typical actions for the session_start event are + requesting the roster and broadcasting an initial + presence stanza. + + Arguments: + event -- An empty dictionary. The session_start + event does not provide any additional + data. + """ + self.getRoster() + self.sendPresence() + self.plugin['xep_0045'].joinMUC(self.room, + self.nick, + # If a room password is needed, use: + # password=the_room_password, + wait=True) + + def muc_message(self, msg): + """ + Process incoming message stanzas from any chat room. Be aware + that if you also have any handlers for the 'message' event, + message stanzas may be processed by both handlers, so check + the 'type' attribute when using a 'message' event handler. + + Whenever the bot's nickname is mentioned, respond to + the message. + + IMPORTANT: Always check that a message is not from yourself, + otherwise you will create an infinite loop responding + to your own messages. + + This handler will reply to messages that mention + the bot's nickname. + + Arguments: + msg -- The received message stanza. See the documentation + for stanza objects and the Message stanza to see + how it may be used. + """ + if msg['mucnick'] != self.nick and self.nick in msg['body']: + self.send_message(mto=msg['from'].bare, + mbody="I heard that, %s." % msg['mucnick'], + mtype='groupchat') + + def muc_online(self, presence): + """ + Process a presence stanza from a chat room. In this case, + presences from users that have just come online are + handled by sending a welcome message that includes + the user's nickname and role in the room. + + Arguments: + presence -- The received presence stanza. See the + documentation for the Presence stanza + to see how else it may be used. + """ + if presence['muc']['nick'] != self.nick: + self.send_message(mto=presence['from'].bare, + mbody="Hello, %s %s" % (presence['muc']['role'], + presence['muc']['nick']), + mtype='groupchat') + + +if __name__ == '__main__': + # Setup the command line arguments. + optp = OptionParser() + + # Output verbosity options. + optp.add_option('-q', '--quiet', help='set logging to ERROR', + action='store_const', dest='loglevel', + const=logging.ERROR, default=logging.INFO) + optp.add_option('-d', '--debug', help='set logging to DEBUG', + action='store_const', dest='loglevel', + const=logging.DEBUG, default=logging.INFO) + optp.add_option('-v', '--verbose', help='set logging to COMM', + action='store_const', dest='loglevel', + const=5, default=logging.INFO) + + # JID and password options. + optp.add_option("-j", "--jid", dest="jid", + help="JID to use") + optp.add_option("-p", "--password", dest="password", + help="password to use") + optp.add_option("-r", "--room", dest="room", + help="MUC room to join") + optp.add_option("-n", "--nick", dest="nick", + help="MUC nickname") + + opts, args = optp.parse_args() + + # Setup logging. + logging.basicConfig(level=opts.loglevel, + format='%(levelname)-8s %(message)s') + + if opts.jid is None: + opts.jid = raw_input("Username: ") + if opts.password is None: + opts.password = getpass.getpass("Password: ") + if opts.room is None: + opts.room = raw_input("MUC room: ") + if opts.nick is None: + opts.nick = raw_input("MUC nickname: ") + + # Setup the MUCBot and register plugins. Note that while plugins may + # have interdependencies, the order in which you register them does + # not matter. + xmpp = MUCBot(opts.jid, opts.password, opts.room, opts.nick) + xmpp.register_plugin('xep_0030') # Service Discovery + xmpp.register_plugin('xep_0045') # Multi-User Chat + xmpp.register_plugin('xep_0199') # XMPP Ping + + # Connect to the XMPP server and start processing XMPP stanzas. + if xmpp.connect(): + # If you do not have the dnspython library installed, you will need + # to manually specify the name of the server if it does not match + # the one in the JID. For example, to use Google Talk you would + # need to use: + # + # if xmpp.connect(('talk.google.com', 5222)): + # ... + xmpp.process(block=True) + print("Done") + else: + print("Unable to connect.") diff --git a/examples/ping.py b/examples/ping.py new file mode 100755 index 0000000..258fd76 --- /dev/null +++ b/examples/ping.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import sys +import logging +import getpass +from optparse import OptionParser + +import sleekxmpp + +# Python versions before 3.0 do not use UTF-8 encoding +# by default. To ensure that Unicode is handled properly +# throughout SleekXMPP, we will set the default encoding +# ourselves to UTF-8. +if sys.version_info < (3, 0): + reload(sys) + sys.setdefaultencoding('utf8') +else: + raw_input = input + + +class PingTest(sleekxmpp.ClientXMPP): + + """ + A simple SleekXMPP bot that will send a ping request + to a given JID. + """ + + def __init__(self, jid, password, pingjid): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + if pingjid is None: + pingjid = self.jid + self.pingjid = pingjid + + # The session_start event will be triggered when + # the bot establishes its connection with the server + # and the XML streams are ready for use. We want to + # listen for this event so that we we can initialize + # our roster. + self.add_event_handler("session_start", self.start) + + def start(self, event): + """ + Process the session_start event. + + Typical actions for the session_start event are + requesting the roster and broadcasting an initial + presence stanza. + + Arguments: + event -- An empty dictionary. The session_start + event does not provide any additional + data. + """ + self.send_presence() + self.get_roster() + result = self['xep_0199'].send_ping(self.pingjid, + timeout=10, + errorfalse=True) + logging.info("Pinging...") + if result is False: + logging.info("Couldn't ping.") + self.disconnect() + sys.exit(1) + else: + logging.info("Success! RTT: %s", str(result)) + self.disconnect() + + +if __name__ == '__main__': + # Setup the command line arguments. + optp = OptionParser() + + # Output verbosity options. + optp.add_option('-q', '--quiet', help='set logging to ERROR', + action='store_const', dest='loglevel', + const=logging.ERROR, default=logging.INFO) + optp.add_option('-d', '--debug', help='set logging to DEBUG', + action='store_const', dest='loglevel', + const=logging.DEBUG, default=logging.INFO) + optp.add_option('-v', '--verbose', help='set logging to COMM', + action='store_const', dest='loglevel', + const=5, default=logging.INFO) + optp.add_option('-t', '--pingto', help='set jid to ping', + action='store', type='string', dest='pingjid', + default=None) + + # JID and password options. + optp.add_option("-j", "--jid", dest="jid", + help="JID to use") + optp.add_option("-p", "--password", dest="password", + help="password to use") + + opts, args = optp.parse_args() + + # Setup logging. + logging.basicConfig(level=opts.loglevel, + format='%(levelname)-8s %(message)s') + + if opts.jid is None: + opts.jid = raw_input("Username: ") + if opts.password is None: + opts.password = getpass.getpass("Password: ") + + # Setup the PingTest and register plugins. Note that while plugins may + # have interdependencies, the order in which you register them does + # not matter. + xmpp = PingTest(opts.jid, opts.password, opts.pingjid) + xmpp.register_plugin('xep_0030') # Service Discovery + xmpp.register_plugin('xep_0004') # Data Forms + xmpp.register_plugin('xep_0060') # PubSub + xmpp.register_plugin('xep_0199') # XMPP Ping + + # If you are working with an OpenFire server, you may need + # to adjust the SSL version used: + # xmpp.ssl_version = ssl.PROTOCOL_SSLv3 + + # If you want to verify the SSL certificates offered by a server: + # xmpp.ca_certs = "path/to/ca/cert" + + # Connect to the XMPP server and start processing XMPP stanzas. + if xmpp.connect(): + # If you do not have the dnspython library installed, you will need + # to manually specify the name of the server if it does not match + # the one in the JID. For example, to use Google Talk you would + # need to use: + # + # if xmpp.connect(('talk.google.com', 5222)): + # ... + xmpp.process(block=True) + print("Done") + else: + print("Unable to connect.") diff --git a/examples/proxy_echo_client.py b/examples/proxy_echo_client.py new file mode 100755 index 0000000..25bfc89 --- /dev/null +++ b/examples/proxy_echo_client.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import sys +import logging +import getpass +from optparse import OptionParser + +import sleekxmpp + +# Python versions before 3.0 do not use UTF-8 encoding +# by default. To ensure that Unicode is handled properly +# throughout SleekXMPP, we will set the default encoding +# ourselves to UTF-8. +if sys.version_info < (3, 0): + reload(sys) + sys.setdefaultencoding('utf8') +else: + raw_input = input + + +class EchoBot(sleekxmpp.ClientXMPP): + + """ + A simple SleekXMPP bot that will echo messages it + receives, along with a short thank you message. + """ + + def __init__(self, jid, password): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + + # The session_start event will be triggered when + # the bot establishes its connection with the server + # and the XML streams are ready for use. We want to + # listen for this event so that we we can initialize + # our roster. + self.add_event_handler("session_start", self.start) + + # The message event is triggered whenever a message + # stanza is received. Be aware that that includes + # MUC messages and error messages. + self.add_event_handler("message", self.message) + + def start(self, event): + """ + Process the session_start event. + + Typical actions for the session_start event are + requesting the roster and broadcasting an initial + presence stanza. + + Arguments: + event -- An empty dictionary. The session_start + event does not provide any additional + data. + """ + self.send_presence() + self.get_roster() + + def message(self, msg): + """ + Process incoming message stanzas. Be aware that this also + includes MUC messages and error messages. It is usually + a good idea to check the messages's type before processing + or sending replies. + + Arguments: + msg -- The received message stanza. See the documentation + for stanza objects and the Message stanza to see + how it may be used. + """ + msg.reply("Thanks for sending\n%(body)s" % msg).send() + + +if __name__ == '__main__': + # Setup the command line arguments. + optp = OptionParser() + + # Output verbosity options. + optp.add_option('-q', '--quiet', help='set logging to ERROR', + action='store_const', dest='loglevel', + const=logging.ERROR, default=logging.INFO) + optp.add_option('-d', '--debug', help='set logging to DEBUG', + action='store_const', dest='loglevel', + const=logging.DEBUG, default=logging.INFO) + optp.add_option('-v', '--verbose', help='set logging to COMM', + action='store_const', dest='loglevel', + const=5, default=logging.INFO) + + # JID and password options. + optp.add_option("-j", "--jid", dest="jid", + help="JID to use") + optp.add_option("-p", "--password", dest="password", + help="password to use") + optp.add_option("--phost", dest="proxy_host", + help="Proxy hostname") + optp.add_option("--pport", dest="proxy_port", + help="Proxy port") + optp.add_option("--puser", dest="proxy_user", + help="Proxy username") + optp.add_option("--ppass", dest="proxy_pass", + help="Proxy password") + + + + opts, args = optp.parse_args() + + # Setup logging. + logging.basicConfig(level=opts.loglevel, + format='%(levelname)-8s %(message)s') + + if opts.jid is None: + opts.jid = raw_input("Username: ") + if opts.password is None: + opts.password = getpass.getpass("Password: ") + if opts.proxy_host is None: + opts.proxy_host = raw_input("Proxy host: ") + if opts.proxy_port is None: + opts.proxy_port = raw_input("Proxy port: ") + if opts.proxy_user is None: + opts.proxy_user = raw_input("Proxy username: ") + if opts.proxy_pass is None and opts.proxy_user: + opts.proxy_pass = getpass.getpass("Proxy password: ") + + # Setup the EchoBot and register plugins. Note that while plugins may + # have interdependencies, the order in which you register them does + # not matter. + xmpp = EchoBot(opts.jid, opts.password) + xmpp.register_plugin('xep_0030') # Service Discovery + xmpp.register_plugin('xep_0004') # Data Forms + xmpp.register_plugin('xep_0060') # PubSub + xmpp.register_plugin('xep_0199') # XMPP Ping + + # If you are working with an OpenFire server, you may need + # to adjust the SSL version used: + # xmpp.ssl_version = ssl.PROTOCOL_SSLv3 + + # If you want to verify the SSL certificates offered by a server: + # xmpp.ca_certs = "path/to/ca/cert" + + xmpp.use_proxy = True + xmpp.proxy_config = { + 'host': opts.proxy_host, + 'port': int(opts.proxy_port), + 'username': opts.proxy_user, + 'password': opts.proxy_pass} + + # Connect to the XMPP server and start processing XMPP stanzas. + if xmpp.connect(): + # If you do not have the dnspython library installed, you will need + # to manually specify the name of the server if it does not match + # the one in the JID. For example, to use Google Talk you would + # need to use: + # + # if xmpp.connect(('talk.google.com', 5222)): + # ... + xmpp.process(block=True) + print("Done") + else: + print("Unable to connect.") diff --git a/examples/roster_browser.py b/examples/roster_browser.py new file mode 100644 index 0000000..b366d00 --- /dev/null +++ b/examples/roster_browser.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import sys +import logging +import getpass +import threading +from optparse import OptionParser + +import sleekxmpp +from sleekxmpp.exceptions import IqError, IqTimeout + + +# Python versions before 3.0 do not use UTF-8 encoding +# by default. To ensure that Unicode is handled properly +# throughout SleekXMPP, we will set the default encoding +# ourselves to UTF-8. +if sys.version_info < (3, 0): + reload(sys) + sys.setdefaultencoding('utf8') +else: + raw_input = input + + +class RosterBrowser(sleekxmpp.ClientXMPP): + + """ + A basic script for dumping a client's roster to + the command line. + """ + + def __init__(self, jid, password): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + # The session_start event will be triggered when + # the bot establishes its connection with the server + # and the XML streams are ready for use. We want to + # listen for this event so that we we can initialize + # our roster. We need threaded=True so that the + # session_start handler doesn't block event processing + # while we wait for presence stanzas to arrive. + self.add_event_handler("session_start", self.start, threaded=True) + self.add_event_handler("changed_status", self.wait_for_presences) + + self.received = set() + self.presences_received = threading.Event() + + def start(self, event): + """ + Process the session_start event. + + Typical actions for the session_start event are + requesting the roster and broadcasting an initial + presence stanza. + + Arguments: + event -- An empty dictionary. The session_start + event does not provide any additional + data. + """ + try: + self.get_roster() + except IqError as err: + print('Error: %' % err.iq['error']['condition']) + except IqTimeout: + print('Error: Request timed out') + self.send_presence() + + + print('Waiting for presence updates...\n') + self.presences_received.wait(5) + + print('Roster for %s' % self.boundjid.bare) + groups = self.client_roster.groups() + for group in groups: + print('\n%s' % group) + print('-' * 72) + for jid in groups[group]: + sub = self.client_roster[jid]['subscription'] + name = self.client_roster[jid]['name'] + if self.client_roster[jid]['name']: + print(' %s (%s) [%s]' % (name, jid, sub)) + else: + print(' %s [%s]' % (jid, sub)) + + connections = self.client_roster.presence(jid) + for res, pres in connections.items(): + show = 'available' + if pres['show']: + show = pres['show'] + print(' - %s (%s)' % (res, show)) + if pres['status']: + print(' %s' % pres['status']) + + self.disconnect() + + def wait_for_presences(self, pres): + """ + Track how many roster entries have received presence updates. + """ + self.received.add(pres['from'].bare) + if len(self.received) >= len(self.client_roster.keys()): + self.presences_received.set() + else: + self.presences_received.clear() + + + +if __name__ == '__main__': + # Setup the command line arguments. + optp = OptionParser() + optp.add_option('-q','--quiet', help='set logging to ERROR', + action='store_const', + dest='loglevel', + const=logging.ERROR, + default=logging.ERROR) + optp.add_option('-d','--debug', help='set logging to DEBUG', + action='store_const', + dest='loglevel', + const=logging.DEBUG, + default=logging.ERROR) + optp.add_option('-v','--verbose', help='set logging to COMM', + action='store_const', + dest='loglevel', + const=5, + default=logging.ERROR) + + # JID and password options. + optp.add_option("-j", "--jid", dest="jid", + help="JID to use") + optp.add_option("-p", "--password", dest="password", + help="password to use") + opts,args = optp.parse_args() + + # Setup logging. + logging.basicConfig(level=opts.loglevel, + format='%(levelname)-8s %(message)s') + + if opts.jid is None: + opts.jid = raw_input("Username: ") + if opts.password is None: + opts.password = getpass.getpass("Password: ") + + xmpp = RosterBrowser(opts.jid, opts.password) + + # If you are working with an OpenFire server, you may need + # to adjust the SSL version used: + # xmpp.ssl_version = ssl.PROTOCOL_SSLv3 + + # If you want to verify the SSL certificates offered by a server: + # xmpp.ca_certs = "path/to/ca/cert" + + # Connect to the XMPP server and start processing XMPP stanzas. + if xmpp.connect(): + # If you do not have the dnspython library installed, you will need + # to manually specify the name of the server if it does not match + # the one in the JID. For example, to use Google Talk you would + # need to use: + # + # if xmpp.connect(('talk.google.com', 5222)): + # ... + xmpp.process(block=True) + else: + print("Unable to connect.") + diff --git a/examples/rpc_async.py b/examples/rpc_async.py new file mode 100644 index 0000000..0b6d193 --- /dev/null +++ b/examples/rpc_async.py @@ -0,0 +1,44 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Dann Martens + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.plugins.xep_0009.remote import Endpoint, remote, Remote, \ + ANY_ALL, Future +import time + +class Boomerang(Endpoint): + + def FQN(self): + return 'boomerang' + + @remote + def throw(self): + print "Duck!" + + + +def main(): + + session = Remote.new_session('kangaroo@xmpp.org/rpc', '*****') + + session.new_handler(ANY_ALL, Boomerang) + + boomerang = session.new_proxy('kangaroo@xmpp.org/rpc', Boomerang) + + callback = Future() + + boomerang.async(callback).throw() + + time.sleep(10) + + session.close() + + + +if __name__ == '__main__': + main() + \ No newline at end of file diff --git a/examples/rpc_client_side.py b/examples/rpc_client_side.py new file mode 100644 index 0000000..ca1084f --- /dev/null +++ b/examples/rpc_client_side.py @@ -0,0 +1,53 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Dann Martens + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.plugins.xep_0009.remote import Endpoint, remote, Remote, \ + ANY_ALL +import threading +import time + +class Thermostat(Endpoint): + + def FQN(self): + return 'thermostat' + + def __init(self, initial_temperature): + self._temperature = initial_temperature + self._event = threading.Event() + + @remote + def set_temperature(self, temperature): + return NotImplemented + + @remote + def get_temperature(self): + return NotImplemented + + @remote(False) + def release(self): + return NotImplemented + + + +def main(): + + session = Remote.new_session('operator@xmpp.org/rpc', '*****') + + thermostat = session.new_proxy('thermostat@xmpp.org/rpc', Thermostat) + + print("Current temperature is %s" % thermostat.get_temperature()) + + thermostat.set_temperature(20) + + time.sleep(10) + + session.close() + +if __name__ == '__main__': + main() + \ No newline at end of file diff --git a/examples/rpc_server_side.py b/examples/rpc_server_side.py new file mode 100644 index 0000000..0af8af4 --- /dev/null +++ b/examples/rpc_server_side.py @@ -0,0 +1,52 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Dann Martens + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.plugins.xep_0009.remote import Endpoint, remote, Remote, \ + ANY_ALL +import threading + +class Thermostat(Endpoint): + + def FQN(self): + return 'thermostat' + + def __init(self, initial_temperature): + self._temperature = initial_temperature + self._event = threading.Event() + + @remote + def set_temperature(self, temperature): + print("Setting temperature to %s" % temperature) + self._temperature = temperature + + @remote + def get_temperature(self): + return self._temperature + + @remote(False) + def release(self): + self._event.set() + + def wait_for_release(self): + self._event.wait() + + + +def main(): + + session = Remote.new_session('sleek@xmpp.org/rpc', '*****') + + thermostat = session.new_handler(ANY_ALL, Thermostat, 18) + + thermostat.wait_for_release() + + session.close() + +if __name__ == '__main__': + main() + \ No newline at end of file diff --git a/examples/send_client.py b/examples/send_client.py new file mode 100755 index 0000000..caf4768 --- /dev/null +++ b/examples/send_client.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import sys +import logging +import getpass +from optparse import OptionParser + +import sleekxmpp + +# Python versions before 3.0 do not use UTF-8 encoding +# by default. To ensure that Unicode is handled properly +# throughout SleekXMPP, we will set the default encoding +# ourselves to UTF-8. +if sys.version_info < (3, 0): + reload(sys) + sys.setdefaultencoding('utf8') +else: + raw_input = input + + +class SendMsgBot(sleekxmpp.ClientXMPP): + + """ + A basic SleekXMPP bot that will log in, send a message, + and then log out. + """ + + def __init__(self, jid, password, recipient, message): + sleekxmpp.ClientXMPP.__init__(self, jid, password) + + # The message we wish to send, and the JID that + # will receive it. + self.recipient = recipient + self.msg = message + + # The session_start event will be triggered when + # the bot establishes its connection with the server + # and the XML streams are ready for use. We want to + # listen for this event so that we we can initialize + # our roster. + self.add_event_handler("session_start", self.start) + + def start(self, event): + """ + Process the session_start event. + + Typical actions for the session_start event are + requesting the roster and broadcasting an initial + presence stanza. + + Arguments: + event -- An empty dictionary. The session_start + event does not provide any additional + data. + """ + self.send_presence() + self.get_roster() + + self.send_message(mto=self.recipient, + mbody=self.msg, + mtype='chat') + + # Using wait=True ensures that the send queue will be + # emptied before ending the session. + self.disconnect(wait=True) + + +if __name__ == '__main__': + # Setup the command line arguments. + optp = OptionParser() + + # Output verbosity options. + optp.add_option('-q', '--quiet', help='set logging to ERROR', + action='store_const', dest='loglevel', + const=logging.ERROR, default=logging.INFO) + optp.add_option('-d', '--debug', help='set logging to DEBUG', + action='store_const', dest='loglevel', + const=logging.DEBUG, default=logging.INFO) + optp.add_option('-v', '--verbose', help='set logging to COMM', + action='store_const', dest='loglevel', + const=5, default=logging.INFO) + + # JID and password options. + optp.add_option("-j", "--jid", dest="jid", + help="JID to use") + optp.add_option("-p", "--password", dest="password", + help="password to use") + optp.add_option("-t", "--to", dest="to", + help="JID to send the message to") + optp.add_option("-m", "--message", dest="message", + help="message to send") + + opts, args = optp.parse_args() + + # Setup logging. + logging.basicConfig(level=opts.loglevel, + format='%(levelname)-8s %(message)s') + + if opts.jid is None: + opts.jid = raw_input("Username: ") + if opts.password is None: + opts.password = getpass.getpass("Password: ") + if opts.to is None: + opts.to = raw_input("Send To: ") + if opts.message is None: + opts.message = raw_input("Message: ") + + # Setup the EchoBot and register plugins. Note that while plugins may + # have interdependencies, the order in which you register them does + # not matter. + xmpp = SendMsgBot(opts.jid, opts.password, opts.to, opts.message) + xmpp.register_plugin('xep_0030') # Service Discovery + xmpp.register_plugin('xep_0199') # XMPP Ping + + # If you are working with an OpenFire server, you may need + # to adjust the SSL version used: + # xmpp.ssl_version = ssl.PROTOCOL_SSLv3 + + # If you want to verify the SSL certificates offered by a server: + # xmpp.ca_certs = "path/to/ca/cert" + + # Connect to the XMPP server and start processing XMPP stanzas. + if xmpp.connect(): + # If you do not have the dnspython library installed, you will need + # to manually specify the name of the server if it does not match + # the one in the JID. For example, to use Google Talk you would + # need to use: + # + # if xmpp.connect(('talk.google.com', 5222)): + # ... + xmpp.process(block=True) + print("Done") + else: + print("Unable to connect.")