Compare commits

..

1 Commits

Author SHA1 Message Date
Lance Stout
846f2bac5f Bump version to 1.1.11 2012-10-31 13:54:38 -07:00
611 changed files with 14916 additions and 23102 deletions

View File

@@ -1,13 +0,0 @@
################ Please use Gitlab instead of Github ###################################
Hello, thank you for contributing to slixmpp!
Youre about to open a pull request on github. However this github repository is not the official place for contributions on slixmpp.
Please open your merge request on https://lab.louiz.org/poezio/slixmpp/
You should be able to log in there with your github credentials, clone the slixmpp repository in your namespace, push your existing pull request into a new branch, and then open a merge request with one click, within 3 minutes.
This will help us review your contribution, avoid spreading things everywhere and it will even run the tests automatically with your changes.
Thank you.

10
.gitignore vendored
View File

@@ -1,4 +1,4 @@
*.py[co] *.pyc
build/ build/
dist/ dist/
MANIFEST MANIFEST
@@ -6,10 +6,4 @@ docs/_build/
*.swp *.swp
.tox/ .tox/
.coverage .coverage
slixmpp.egg-info/ sleekxmpp.egg-info/
.ropeproject/
4913
*~
.baboon/
.DS_STORE
.idea/

View File

@@ -1,21 +0,0 @@
stages:
- test
- trigger
test:
stage: test
tags:
- docker
image: ubuntu:latest
script:
- apt update
- apt install -y python3 cython3 gpg
- ./run_tests.py
trigger_poezio:
stage: trigger
tags:
- docker
image: appropriate/curl:latest
script:
- curl --request POST -F token="$SLIXMPP_TRIGGER_TOKEN" -F ref=master https://lab.louiz.org/api/v4/projects/18/trigger/pipeline

View File

@@ -1,7 +0,0 @@
language: python
python:
- "3.7"
- "3.8-dev"
install:
- "pip install ."
script: testall.py

View File

@@ -1,14 +0,0 @@
Contributing to the Slixmpp project
===================================
To contribute, the preferred way is to commit your changes on some
publicly-available git repository (on a fork `on github
<https://github.com/poezio/slixmpp>`_ or on your own repository) and to
notify the developers with either:
- a ticket `on the bug tracker <https://lab.louiz.org/poezio/slixmpp/issues/new>`_
- a pull request on github
- a simple message on `the XMPP MUC <xmpp:slixmpp@muc.poez.io>`_
Even though Slixmpps github repository is just a read-only mirror, we can
still be notified of the pull requests and fetch your mirror manually to
integrate your changes.

View File

@@ -1,7 +1,5 @@
Pre-requisites: Pre-requisites:
- Python 3.7+ - Python 3.1 or 2.6
- Cython 0.22 and libidn, optionally (making JID faster by compiling the stringprep module)
- GnuPG, for testing
Install: Install:
> python3 setup.py install > python3 setup.py install
@@ -11,4 +9,4 @@ Root install:
To test: To test:
> cd examples > cd examples
> python3 echo_client.py -d -j [USER@example.com] -p [PASSWORD] > python echo_client.py -v -j [USER@example.com] -p [PASSWORD]

View File

@@ -69,8 +69,8 @@ modification, are permitted provided that the following conditions are met:
* Redistributions in binary form must reproduce the above copyright * Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
* Neither the name of Red Innovation nor the names of its contributors * Neither the name of Red Innovation nor the names of its contributors
may be used to endorse or promote products derived from this software may be used to endorse or promote products derived from this software
without specific prior written permission. without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY RED INNOVATION ``AS IS'' AND ANY THIS SOFTWARE IS PROVIDED BY RED INNOVATION ``AS IS'' AND ANY
@@ -119,7 +119,7 @@ SUELTA A PURE-PYTHON SASL CLIENT LIBRARY
This software is subject to "The MIT License" This software is subject to "The MIT License"
Copyright 2004-2013 David Alan Cridland Copyright 2007-2010 David Alan Cridland
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,6 @@
include README.rst include README.rst
include LICENSE include LICENSE
include run_tests.py include testall.py
include slixmpp/stringprep.pyx
recursive-include docs Makefile *.bat *.py *.rst *.css *.ttf *.png recursive-include docs Makefile *.bat *.py *.rst *.css *.ttf *.png
recursive-include examples *.py recursive-include examples *.py
recursive-include tests *.py recursive-include tests *.py

View File

@@ -1,21 +1,77 @@
Slixmpp SleekXMPP
######### #########
Slixmpp is an MIT licensed XMPP library for Python 3.7+. It is a fork of SleekXMPP is an MIT licensed XMPP library for Python 2.6/3.1+,
SleekXMPP. and is featured in examples in
`XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271>`_
by Kevin Smith, Remko Tronçon, and Peter Saint-Andre. If you've arrived
here from reading the Definitive Guide, please see the notes on updating
the examples to the latest version of SleekXMPP.
Slixmpp's goals is to only rewrite the core of the library (the low level SleekXMPP's design goals and philosphy are:
socket handling, the timers, the events dispatching) in order to remove all
threads.
Building **Low number of dependencies**
-------- Installing and using SleekXMPP should be as simple as possible, without
having to deal with long dependency chains.
Slixmpp can make use of cython to improve performance on critical modules. As part of reducing the number of dependencies, some third party
To do that, **cython3** is necessary along with **libidn** headers. modules are included with SleekXMPP in the ``thirdparty`` directory.
Otherwise, no compilation is needed. Building is done by running setup.py:: Imports from this module first try to import an existing installed
version before loading the packaged version, when possible.
**Every XEP as a plugin**
Following Python's "batteries included" approach, the goal is to
provide support for all currently active XEPs (final and draft). Since
adding XEP support is done through easy to create plugins, the hope is
to also provide a solid base for implementing and creating experimental
XEPs.
**Rewarding to work with**
As much as possible, SleekXMPP should allow things to "just work" using
sensible defaults and appropriate abstractions. XML can be ugly to work
with, but it doesn't have to be that way.
Get the Code
------------
Get the latest stable version from PyPI::
pip install sleekxmpp
The latest source code for SleekXMPP may be found on `Github
<http://github.com/fritzy/SleekXMPP>`_. Releases can be found in the
``master`` branch, while the latest development version is in the
``develop`` branch.
**Latest Release**
- `1.1.10 <http://github.com/fritzy/SleekXMPP/zipball/1.1.10>`_
**Develop Releases**
- `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_
Installing DNSPython
---------------------
If you are using Python3 and wish to use dnspython, you will have to checkout and
install the ``python3`` branch::
git clone http://github.com/rthalley/dnspython
cd dnspython
git checkout python3
python3 setup.py install
Discussion
----------
A mailing list and XMPP chat room are available for discussing and getting
help with SleekXMPP.
**Mailing List**
`SleekXMPP Discussion on Google Groups <http://groups.google.com/group/sleekxmpp-discussion>`_
**Chat**
`sleek@conference.jabber.org <xmpp:sleek@conference.jabber.org?join>`_
python3 setup.py build_ext --inplace
Documentation and Testing Documentation and Testing
------------------------- -------------------------
@@ -27,22 +83,22 @@ be in ``docs/_build/html``::
make html make html
open _build/html/index.html open _build/html/index.html
To run the test suite for Slixmpp:: To run the test suite for SleekXMPP::
python run_tests.py python testall.py
The Slixmpp Boilerplate The SleekXMPP Boilerplate
------------------------- -------------------------
Projects using Slixmpp tend to follow a basic pattern for setting up client/component Projects using SleekXMPP tend to follow a basic pattern for setting up client/component
connections and configuration. Here is the gist of the boilerplate needed for a Slixmpp connections and configuration. Here is the gist of the boilerplate needed for a SleekXMPP
based project. See the documentation or examples directory for more detailed archetypes for based project. See the documetation or examples directory for more detailed archetypes for
Slixmpp projects:: SleekXMPP projects::
import logging import logging
from slixmpp import ClientXMPP from sleekxmpp import ClientXMPP
from slixmpp.exceptions import IqError, IqTimeout from sleekxmpp.exceptions import IqError, IqTimeout
class EchoBot(ClientXMPP): class EchoBot(ClientXMPP):
@@ -88,7 +144,7 @@ Slixmpp projects::
if __name__ == '__main__': if __name__ == '__main__':
# Ideally use optparse or argparse to get JID, # Ideally use optparse or argparse to get JID,
# password, and log level. # password, and log level.
logging.basicConfig(level=logging.DEBUG, logging.basicConfig(level=logging.DEBUG,
@@ -96,38 +152,21 @@ Slixmpp projects::
xmpp = EchoBot('somejid@example.com', 'use_getpass') xmpp = EchoBot('somejid@example.com', 'use_getpass')
xmpp.connect() xmpp.connect()
xmpp.process(forever=True) xmpp.process(block=True)
Slixmpp Credits Credits
--------------- -------
**Maintainers:**
- Florent Le Coz (`louiz@louiz.org <xmpp:louiz@louiz.org?message>`_),
- Mathieu Pasquet (`mathieui@mathieui.net <xmpp:mathieui@mathieui.net?message>`_),
**Contributors:**
- Emmanuel Gil Peyrot (`Link mauve <xmpp:linkmauve@linkmauve.fr?message>`_)
- Sam Whited (`Sam Whited <mailto:sam@samwhited.com>`_)
- Dan Sully (`Dan Sully <mailto:daniel@electricalrain.com>`_)
- Gasper Zejn (`Gasper Zejn <mailto:zejn@kiberpipa.org>`_)
- Krzysztof Kotlenga (`Krzysztof Kotlenga <mailto:pocek@users.sf.net>`_)
- Tsukasa Hiiragi (`Tsukasa Hiiragi <mailto:bakalolka@gmail.com>`_)
- Maxime Buquet (`pep <xmpp:pep@bouah.net?message>`_)
Credits (SleekXMPP)
-------------------
**Main Author:** Nathan Fritz **Main Author:** Nathan Fritz
`fritzy@netflint.net <xmpp:fritzy@netflint.net?message>`_, `fritzy@netflint.net <xmpp:fritzy@netflint.net?message>`_,
`@fritzy <http://twitter.com/fritzy>`_ `@fritzy <http://twitter.com/fritzy>`_
Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP
<http://code.google.com/p/seesmic-as3-xmpp/>`_, and a former member of <http://code.google.com/p/seesmic-as3-xmpp/>`_, and a former member of
the XMPP Council. the XMPP Council.
**Co-Author:** Lance Stout **Co-Author:** Lance Stout
`lancestout@gmail.com <xmpp:lancestout@gmail.com?message>`_, `lancestout@gmail.com <xmpp:lancestout@gmail.com?message>`_,
`@lancestout <http://twitter.com/lancestout>`_ `@lancestout <http://twitter.com/lancestout>`_
**Contributors:** **Contributors:**

View File

@@ -72,17 +72,17 @@ qthelp:
@echo @echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:" ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Slixmpp.qhcp" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SleekXMPP.qhcp"
@echo "To view the help file:" @echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Slixmpp.qhc" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SleekXMPP.qhc"
devhelp: devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo @echo
@echo "Build finished." @echo "Build finished."
@echo "To view the help file:" @echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/Slixmpp" @echo "# mkdir -p $$HOME/.local/share/devhelp/SleekXMPP"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Slixmpp" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SleekXMPP"
@echo "# devhelp" @echo "# devhelp"
epub: epub:

View File

@@ -408,3 +408,24 @@ div.viewcode-block:target {
margin: -1px -12px; margin: -1px -12px;
padding: 0 12px; padding: 0 12px;
} }
#from_andyet {
-webkit-box-shadow: #CCC 0px 0px 3px;
background: rgba(255, 255, 255, 1);
bottom: 0px;
right: 17px;
padding: 3px 10px;
position: fixed;
}
#from_andyet h2 {
background-image: url("images/from_&yet.png");
background-repeat: no-repeat;
height: 29px;
line-height: 0;
text-indent: -9999em;
width: 79px;
margin-top: 0;
margin: 0px;
padding: 0px;
}

View File

@@ -8,11 +8,11 @@
* :license: BSD, see LICENSE for details. * :license: BSD, see LICENSE for details.
* *
*/ */
@import url("basic.css"); @import url("basic.css");
/* -- page layout ----------------------------------------------------------- */ /* -- page layout ----------------------------------------------------------- */
body { body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
font-size: 100%; font-size: 100%;
@@ -34,18 +34,18 @@ div.bodywrapper {
hr { hr {
border: 1px solid #B1B4B6; border: 1px solid #B1B4B6;
} }
div.document { div.document {
background-color: #eee; background-color: #eee;
} }
div.body { div.body {
background-color: #ffffff; background-color: #ffffff;
color: #3E4349; color: #3E4349;
padding: 0 30px 30px 30px; padding: 0 30px 30px 30px;
font-size: 0.9em; font-size: 0.9em;
} }
div.footer { div.footer {
color: #555; color: #555;
width: 100%; width: 100%;
@@ -53,12 +53,12 @@ div.footer {
text-align: center; text-align: center;
font-size: 75%; font-size: 75%;
} }
div.footer a { div.footer a {
color: #444; color: #444;
text-decoration: underline; text-decoration: underline;
} }
div.related { div.related {
background-color: #6BA81E; background-color: #6BA81E;
line-height: 32px; line-height: 32px;
@@ -66,11 +66,11 @@ div.related {
text-shadow: 0px 1px 0 #444; text-shadow: 0px 1px 0 #444;
font-size: 0.9em; font-size: 0.9em;
} }
div.related a { div.related a {
color: #E2F3CC; color: #E2F3CC;
} }
div.sphinxsidebar { div.sphinxsidebar {
font-size: 0.75em; font-size: 0.75em;
line-height: 1.5em; line-height: 1.5em;
@@ -79,7 +79,7 @@ div.sphinxsidebar {
div.sphinxsidebarwrapper{ div.sphinxsidebarwrapper{
padding: 20px 0; padding: 20px 0;
} }
div.sphinxsidebar h3, div.sphinxsidebar h3,
div.sphinxsidebar h4 { div.sphinxsidebar h4 {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
@@ -95,30 +95,30 @@ div.sphinxsidebar h4 {
div.sphinxsidebar h4{ div.sphinxsidebar h4{
font-size: 1.1em; font-size: 1.1em;
} }
div.sphinxsidebar h3 a { div.sphinxsidebar h3 a {
color: #444; color: #444;
} }
div.sphinxsidebar p { div.sphinxsidebar p {
color: #888; color: #888;
padding: 5px 20px; padding: 5px 20px;
} }
div.sphinxsidebar p.topless { div.sphinxsidebar p.topless {
} }
div.sphinxsidebar ul { div.sphinxsidebar ul {
margin: 10px 20px; margin: 10px 20px;
padding: 0; padding: 0;
color: #000; color: #000;
} }
div.sphinxsidebar a { div.sphinxsidebar a {
color: #444; color: #444;
} }
div.sphinxsidebar input { div.sphinxsidebar input {
border: 1px solid #ccc; border: 1px solid #ccc;
font-family: sans-serif; font-family: sans-serif;
@@ -128,19 +128,19 @@ div.sphinxsidebar input {
div.sphinxsidebar input[type=text]{ div.sphinxsidebar input[type=text]{
margin-left: 20px; margin-left: 20px;
} }
/* -- body styles ----------------------------------------------------------- */ /* -- body styles ----------------------------------------------------------- */
a { a {
color: #005B81; color: #005B81;
text-decoration: none; text-decoration: none;
} }
a:hover { a:hover {
color: #E32E00; color: #E32E00;
text-decoration: underline; text-decoration: underline;
} }
div.body h1, div.body h1,
div.body h2, div.body h2,
div.body h3, div.body h3,
@@ -155,30 +155,30 @@ div.body h6 {
padding: 5px 0 5px 10px; padding: 5px 0 5px 10px;
text-shadow: 0px 1px 0 white text-shadow: 0px 1px 0 white
} }
div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; } div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
div.body h2 { font-size: 150%; background-color: #C8D5E3; } div.body h2 { font-size: 150%; background-color: #C8D5E3; }
div.body h3 { font-size: 120%; background-color: #D8DEE3; } div.body h3 { font-size: 120%; background-color: #D8DEE3; }
div.body h4 { font-size: 110%; background-color: #D8DEE3; } div.body h4 { font-size: 110%; background-color: #D8DEE3; }
div.body h5 { font-size: 100%; background-color: #D8DEE3; } div.body h5 { font-size: 100%; background-color: #D8DEE3; }
div.body h6 { font-size: 100%; background-color: #D8DEE3; } div.body h6 { font-size: 100%; background-color: #D8DEE3; }
a.headerlink { a.headerlink {
color: #c60f0f; color: #c60f0f;
font-size: 0.8em; font-size: 0.8em;
padding: 0 4px 0 4px; padding: 0 4px 0 4px;
text-decoration: none; text-decoration: none;
} }
a.headerlink:hover { a.headerlink:hover {
background-color: #c60f0f; background-color: #c60f0f;
color: white; color: white;
} }
div.body p, div.body dd, div.body li { div.body p, div.body dd, div.body li {
line-height: 1.5em; line-height: 1.5em;
} }
div.admonition p.admonition-title + p { div.admonition p.admonition-title + p {
display: inline; display: inline;
} }
@@ -191,29 +191,29 @@ div.note {
background-color: #eee; background-color: #eee;
border: 1px solid #ccc; border: 1px solid #ccc;
} }
div.seealso { div.seealso {
background-color: #ffc; background-color: #ffc;
border: 1px solid #ff6; border: 1px solid #ff6;
} }
div.topic { div.topic {
background-color: #eee; background-color: #eee;
} }
div.warning { div.warning {
background-color: #ffe4e4; background-color: #ffe4e4;
border: 1px solid #f66; border: 1px solid #f66;
} }
p.admonition-title { p.admonition-title {
display: inline; display: inline;
} }
p.admonition-title:after { p.admonition-title:after {
content: ":"; content: ":";
} }
pre { pre {
padding: 10px; padding: 10px;
background-color: White; background-color: White;
@@ -225,7 +225,7 @@ pre {
-webkit-box-shadow: 1px 1px 1px #d8d8d8; -webkit-box-shadow: 1px 1px 1px #d8d8d8;
-moz-box-shadow: 1px 1px 1px #d8d8d8; -moz-box-shadow: 1px 1px 1px #d8d8d8;
} }
tt { tt {
background-color: #ecf0f3; background-color: #ecf0f3;
color: #222; color: #222;

View File

@@ -134,7 +134,7 @@ div.footer a {
/* -- body styles ----------------------------------------------------------- */ /* -- body styles ----------------------------------------------------------- */
p { p {
margin: 0.8em 0 0.5em 0; margin: 0.8em 0 0.5em 0;
} }

View File

@@ -65,5 +65,6 @@
<div class="bottomnav"> <div class="bottomnav">
{{ nav() }} {{ nav() }}
</div> </div>
<a id="from_andyet" href="http://andyet.net"><h2>From &amp;yet</h2></a>
{% endblock %} {% endblock %}

View File

@@ -2,7 +2,7 @@
BaseXMPP BaseXMPP
======== ========
.. module:: slixmpp.basexmpp .. module:: sleekxmpp.basexmpp
.. autoclass:: BaseXMPP .. autoclass:: BaseXMPP
:members: :members:

View File

@@ -2,7 +2,7 @@
ClientXMPP ClientXMPP
========== ==========
.. module:: slixmpp.clientxmpp .. module:: sleekxmpp.clientxmpp
.. autoclass:: ClientXMPP .. autoclass:: ClientXMPP
:members: :members:

View File

@@ -2,7 +2,7 @@
ComponentXMPP ComponentXMPP
============= =============
.. module:: slixmpp.componentxmpp .. module:: sleekxmpp.componentxmpp
.. autoclass:: ComponentXMPP .. autoclass:: ComponentXMPP
:members: :members:

View File

@@ -1,9 +1,9 @@
Exceptions Exceptions
========== ==========
.. module:: slixmpp.exceptions .. module:: sleekxmpp.exceptions
.. autoexception:: XMPPError .. autoexception:: XMPPError
:members: :members:

View File

@@ -1,8 +0,0 @@
IQ Stanza
=========
.. module:: slixmpp.stanza
.. autoclass:: Iq
:members:

View File

@@ -1,7 +0,0 @@
Message Stanza
==============
.. module:: slixmpp.stanza
.. autoclass:: Message
:members:

View File

@@ -1,8 +0,0 @@
Presence Stanza
===============
.. module:: slixmpp.stanza
.. autoclass:: Presence
:members:

View File

@@ -1,8 +0,0 @@
Root Stanza
===========
.. module:: slixmpp.stanza.rootstanza
.. autoclass:: RootStanza
:members:

View File

@@ -0,0 +1,12 @@
.. module:: sleekxmpp.xmlstream.filesocket
.. _filesocket:
Python 2.6 File Socket Shims
============================
.. autoclass:: FileSocket
:members:
.. autoclass:: Socket26
:members:

View File

@@ -3,26 +3,22 @@ Stanza Handlers
The Basic Handler The Basic Handler
----------------- -----------------
.. module:: slixmpp.xmlstream.handler.base .. module:: sleekxmpp.xmlstream.handler.base
.. autoclass:: BaseHandler .. autoclass:: BaseHandler
:members: :members:
Callback Callback
-------- --------
.. module:: slixmpp.xmlstream.handler .. module:: sleekxmpp.xmlstream.handler.callback
.. autoclass:: Callback .. autoclass:: Callback
:members: :members:
CoroutineCallback
-----------------
.. autoclass:: CoroutineCallback
:members:
Waiter Waiter
------ ------
.. module:: sleekxmpp.xmlstream.handler.waiter
.. autoclass:: Waiter .. autoclass:: Waiter
:members: :members:

View File

@@ -1,7 +1,7 @@
Jabber IDs (JID) Jabber IDs (JID)
================= =================
.. module:: slixmpp.jid .. module:: sleekxmpp.xmlstream.jid
.. autoclass:: JID .. autoclass:: JID
:members: :members:

View File

@@ -3,7 +3,7 @@ Stanza Matchers
The Basic Matcher The Basic Matcher
----------------- -----------------
.. module:: slixmpp.xmlstream.matcher.base .. module:: sleekxmpp.xmlstream.matcher.base
.. autoclass:: MatcherBase .. autoclass:: MatcherBase
:members: :members:
@@ -11,7 +11,7 @@ The Basic Matcher
ID Matching ID Matching
----------- -----------
.. module:: slixmpp.xmlstream.matcher.id .. module:: sleekxmpp.xmlstream.matcher.id
.. autoclass:: MatcherId .. autoclass:: MatcherId
:members: :members:
@@ -19,7 +19,7 @@ ID Matching
Stanza Path Matching Stanza Path Matching
-------------------- --------------------
.. module:: slixmpp.xmlstream.matcher.stanzapath .. module:: sleekxmpp.xmlstream.matcher.stanzapath
.. autoclass:: StanzaPath .. autoclass:: StanzaPath
:members: :members:
@@ -27,7 +27,7 @@ Stanza Path Matching
XPath XPath
----- -----
.. module:: slixmpp.xmlstream.matcher.xpath .. module:: sleekxmpp.xmlstream.matcher.xpath
.. autoclass:: MatchXPath .. autoclass:: MatchXPath
:members: :members:
@@ -35,7 +35,7 @@ XPath
XMLMask XMLMask
------- -------
.. module:: slixmpp.xmlstream.matcher.xmlmask .. module:: sleekxmpp.xmlstream.matcher.xmlmask
.. autoclass:: MatchXMLMask .. autoclass:: MatchXMLMask
:members: :members:

View File

@@ -0,0 +1,11 @@
=========
Scheduler
=========
.. module:: sleekxmpp.xmlstream.scheduler
.. autoclass:: Task
:members:
.. autoclass:: Scheduler
:members:

View File

@@ -4,9 +4,9 @@
Stanza Objects Stanza Objects
============== ==============
.. module:: slixmpp.xmlstream.stanzabase .. module:: sleekxmpp.xmlstream.stanzabase
The :mod:`~slixmpp.xmlstream.stanzabase` module provides a wrapper for the The :mod:`~sleekmxpp.xmlstream.stanzabase` module provides a wrapper for the
standard :mod:`~xml.etree.ElementTree` module that makes working with XML standard :mod:`~xml.etree.ElementTree` module that makes working with XML
less painful. Instead of having to manually move up and down an element less painful. Instead of having to manually move up and down an element
tree and insert subelements and attributes, you can interact with an object tree and insert subelements and attributes, you can interact with an object
@@ -52,17 +52,17 @@ elements of the original XML chunk.
.. seealso:: .. seealso::
:ref:`create-stanza-interfaces`. :ref:`create-stanza-interfaces`.
Because the :mod:`~slixmpp.xmlstream.stanzabase` module was developed Because the :mod:`~sleekxmpp.xmlstream.stanzabase` module was developed
as part of an `XMPP <http://xmpp.org>`_ library, these chunks of XML are as part of an `XMPP <http://xmpp.org>`_ library, these chunks of XML are
referred to as :term:`stanzas <stanza>`, and in Slixmpp we refer to a referred to as :term:`stanzas <stanza>`, and in SleekXMPP we refer to a
subclass of :class:`ElementBase` which defines the interfaces needed for subclass of :class:`ElementBase` which defines the interfaces needed for
interacting with a given :term:`stanza` a :term:`stanza object`. interacting with a given :term:`stanza` a :term:`stanza object`.
To make dealing with more complicated and nested :term:`stanzas <stanza>` To make dealing with more complicated and nested :term:`stanzas <stanza>`
or XML chunks easier, :term:`stanza objects <stanza object>` can be or XML chunks easier, :term:`stanza objects <stanza object>` can be
composed in two ways: as iterable child objects or as plugins. Iterable composed in two ways: as iterable child objects or as plugins. Iterable
child stanzas, or :term:`substanzas <substanza>`, are accessible through a child stanzas, or :term:`substanzas`, are accessible through a special
special ``'substanzas'`` interface. This option is useful for stanzas which ``'substanzas'`` interface. This option is useful for stanzas which
may contain more than one of the same kind of element. When there is may contain more than one of the same kind of element. When there is
only one child element, the plugin method is more useful. For plugins, only one child element, the plugin method is more useful. For plugins,
a parent stanza object delegates one of its XML child elements to the a parent stanza object delegates one of its XML child elements to the
@@ -72,7 +72,7 @@ plugin stanza object. Here is an example:
<iq type="result"> <iq type="result">
<query xmlns="http://jabber.org/protocol/disco#info"> <query xmlns="http://jabber.org/protocol/disco#info">
<identity category="client" type="bot" name="Slixmpp Bot" /> <identity category="client" type="bot" name="SleekXMPP Bot" />
</query> </query>
</iq> </iq>
@@ -84,13 +84,13 @@ we can access the plugin as so::
>>> iq['disco_info'] >>> iq['disco_info']
'<query xmlns="http://jabber.org/protocol/disco#info"> '<query xmlns="http://jabber.org/protocol/disco#info">
<identity category="client" type="bot" name="Slixmpp Bot" /> <identity category="client" type="bot" name="SleekXMPP Bot" />
</query>' </query>'
We can then drill down through the plugin object's interfaces as desired:: We can then drill down through the plugin object's interfaces as desired::
>>> iq['disco_info']['identities'] >>> iq['disco_info']['identities']
[('client', 'bot', 'Slixmpp Bot')] [('client', 'bot', 'SleekXMPP Bot')]
Plugins may also add new interfaces to the parent stanza object as if they Plugins may also add new interfaces to the parent stanza object as if they
had been defined by the parent directly, and can also override the behaviour had been defined by the parent directly, and can also override the behaviour
@@ -101,7 +101,7 @@ of an interface defined by the parent.
- :ref:`create-stanza-plugins` - :ref:`create-stanza-plugins`
- :ref:`create-extension-plugins` - :ref:`create-extension-plugins`
- :ref:`override-parent-interfaces` - :ref:`override-parent-interfaces`
Registering Stanza Plugins Registering Stanza Plugins
-------------------------- --------------------------

View File

@@ -1,19 +1,19 @@
.. module:: slixmpp.xmlstream.tostring .. module:: sleekxmpp.xmlstream.tostring
.. _tostring: .. _tostring:
XML Serialization XML Serialization
================= =================
Since the XML layer of Slixmpp is based on :mod:`~xml.etree.ElementTree`, Since the XML layer of SleekXMPP is based on :mod:`~xml.etree.ElementTree`,
why not just use the built-in :func:`~xml.etree.ElementTree.tostring` why not just use the built-in :func:`~xml.etree.ElementTree.tostring`
method? The answer is that using that method produces ugly results when method? The answer is that using that method produces ugly results when
using namespaces. The :func:`tostring()` method used here intelligently using namespaces. The :func:`tostring()` method used here intelligently
hides namespaces when able and does not introduce excessive namespace hides namespaces when able and does not introduce excessive namespace
prefixes:: prefixes::
>>> from slixmpp.xmlstream.tostring import tostring >>> from sleekxmpp.xmlstream.tostring import tostring
>>> from xml.etree import ElementTree as ET >>> from xml.etree import cElementTree as ET
>>> xml = ET.fromstring('<foo xmlns="bar"><baz /></foo>') >>> xml = ET.fromstring('<foo xmlns="bar"><baz /></foo>')
>>> ET.tostring(xml) >>> ET.tostring(xml)
'<ns0:foo xmlns:ns0="bar"><ns0:baz /></foo>' '<ns0:foo xmlns:ns0="bar"><ns0:baz /></foo>'
@@ -25,10 +25,10 @@ produce unexpected results depending on how the :func:`tostring()` method
is invoked. For example, when sending XML on the wire, the main XMPP is invoked. For example, when sending XML on the wire, the main XMPP
stanzas with their namespace of ``jabber:client`` will not include the stanzas with their namespace of ``jabber:client`` will not include the
namespace because that is already declared by the stream header. But, if namespace because that is already declared by the stream header. But, if
you create a :class:`~slixmpp.stanza.message.Message` instance and dump you create a :class:`~sleekxmpp.stanza.message.Message` instance and dump
it to the terminal, the ``jabber:client`` namespace will appear. it to the terminal, the ``jabber:client`` namespace will appear.
.. autofunction:: slixmpp.xmlstream.tostring .. autofunction:: tostring
Escaping Special Characters Escaping Special Characters
--------------------------- ---------------------------
@@ -43,5 +43,4 @@ In the future, the use of CDATA sections may be allowed to reduce the
size of escaped text or for when other XMPP processing agents do not size of escaped text or for when other XMPP processing agents do not
undertand these entities. undertand these entities.
.. .. autofunction:: xml_escape
autofunction:: xml_escape

View File

@@ -2,7 +2,9 @@
XML Stream XML Stream
========== ==========
.. module:: slixmpp.xmlstream.xmlstream .. module:: sleekxmpp.xmlstream.xmlstream
.. autoexception:: RestartStream
.. autoclass:: XMLStream .. autoclass:: XMLStream
:members: :members:

View File

@@ -1,9 +1,9 @@
.. index:: XMLStream, BaseXMPP, ClientXMPP, ComponentXMPP .. index:: XMLStream, BaseXMPP, ClientXMPP, ComponentXMPP
Slixmpp Architecture SleekXMPP Architecture
====================== ======================
The core of Slixmpp is contained in four classes: ``XMLStream``, The core of SleekXMPP is contained in four classes: ``XMLStream``,
``BaseXMPP``, ``ClientXMPP``, and ``ComponentXMPP``. Along side this ``BaseXMPP``, ``ClientXMPP``, and ``ComponentXMPP``. Along side this
stack is a library for working with XML objects that eliminates most stack is a library for working with XML objects that eliminates most
of the tedium of creating/manipulating XML. of the tedium of creating/manipulating XML.
@@ -17,27 +17,28 @@ of the tedium of creating/manipulating XML.
The Foundation: XMLStream The Foundation: XMLStream
------------------------- -------------------------
:class:`~slixmpp.xmlstream.xmlstream.XMLStream` is a mostly XMPP-agnostic :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream` is a mostly XMPP-agnostic
class whose purpose is to read and write from a bi-directional XML stream. class whose purpose is to read and write from a bi-directional XML stream.
It also allows for callback functions to execute when XML matching given It also allows for callback functions to execute when XML matching given
patterns is received; these callbacks are also referred to as :term:`stream patterns is received; these callbacks are also referred to as :term:`stream
handlers <stream handler>`. The class also provides a basic eventing system handlers <stream handler>`. The class also provides a basic eventing system
which can be triggered either manually or on a timed schedule. which can be triggered either manually or on a timed schedule.
The event loop The Main Threads
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
:class:`~slixmpp.xmlstream.xmlstream.XMLStream` instances inherit the :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream` instances run using at
:class:`asyncio.BaseProtocol` class, and therefore do not have to handle least three background threads: the send thread, the read thread, and the
reads and writes directly, but receive data through scheduler thread. The send thread is in charge of monitoring the send queue
:meth:`~slixmpp.xmlstream.xmlstream.XMLStream.data_received` and write and writing text to the outgoing XML stream. The read thread pulls text off
data in the socket transport. of the incoming XML stream and stores the results in an event queue. The
scheduler thread is used to emit events after a given period of time.
Upon receiving data, :term:`stream handlers <stream handler>` are run Additionally, the main event processing loop may be executed in its
immediately, except if they are coroutines, in which case they are own thread if SleekXMPP is being used in the background for another
scheduled using :meth:`asyncio.async`. application.
:term:`Event handlers <event handler>` (which are called inside Short-lived threads may also be spawned as requested for threaded
:term:`stream handlers <stream handler>`) work the same way. :term:`event handlers <event handler>`.
How XML Text is Turned into Action How XML Text is Turned into Action
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -52,7 +53,7 @@ when this bit of XML is received (with an assumed namespace of
</message> </message>
#. **Convert XML strings into objects.** 1. **Convert XML strings into objects.**
Incoming text is parsed and converted into XML objects (using Incoming text is parsed and converted into XML objects (using
ElementTree) which are then wrapped into what are referred to as ElementTree) which are then wrapped into what are referred to as
@@ -60,69 +61,95 @@ when this bit of XML is received (with an assumed namespace of
new object is determined using a map of namespaced element names to new object is determined using a map of namespaced element names to
classes. classes.
Our incoming XML is thus turned into a :class:`~slixmpp.stanza.Message` Our incoming XML is thus turned into a :class:`~sleekxmpp.stanza.Message`
:term:`stanza object` because the namespaced element name :term:`stanza object` because the namespaced element name
``{jabber:client}message`` is associated with the class ``{jabber:client}message`` is associated with the class
:class:`~slixmpp.stanza.Message`. :class:`~sleekxmpp.stanza.Message`.
#. **Match stanza objects to callbacks.** 2. **Match stanza objects to callbacks.**
These objects are then compared against the stored patterns associated These objects are then compared against the stored patterns associated
with the registered callback handlers. with the registered callback handlers. For each match, a copy of the
:term:`stanza object` is paired with a reference to the handler and
placed into the event queue.
Each handler matching our :term:`stanza object` is then added to a list. Our :class:`~sleekxmpp.stanza.Message` object is thus paired with the message stanza handler
:meth:`BaseXMPP._handle_message` to create the tuple::
#. **Processing callbacks** ('stanza', stanza_obj, handler)
Every handler in the list is then called with the :term:`stanza object` 3. **Process the event queue.**
as a parameter; if the handler is a
:class:`~slixmpp.xmlstream.handler.CoroutineCallback`
then it will be scheduled in the event loop using :meth:`asyncio.async`
instead of run.
#. **Raise Custom Events** The event queue is the heart of SleekXMPP. Nearly every action that
takes place is first inserted into this queue, whether that be received
stanzas, custom events, or scheduled events.
When the stanza is pulled out of the event queue with an associated
callback, the callback function is executed with the stanza as its only
parameter.
.. warning::
The callback, aka :term:`stream handler`, is executed in the main event
processing thread. If the handler blocks, event processing will also
block.
4. **Raise Custom Events**
Since a :term:`stream handler` shouldn't block, if extensive processing Since a :term:`stream handler` shouldn't block, if extensive processing
for a stanza is required (such as needing to send and receive an for a stanza is required (such as needing to send and receive an
:class:`~slixmpp.stanza.Iq` stanza), then custom events must be used. :class:`~sleekxmpp.stanza.Iq` stanza), then custom events must be used.
These events are not explicitly tied to the incoming XML stream and may These events are not explicitly tied to the incoming XML stream and may
be raised at any time. be raised at any time. Importantly, these events may be handled in their
own thread.
In contrast to :term:`stream handlers <stream handler>`, these functions When the event is raised, a copy of the stanza is created for each
are referred to as :term:`event handlers <event handler>`. handler registered for the event. In contrast to :term:`stream handlers
<stream handler>`, these functions are referred to as :term:`event
The code for :meth:`BaseXMPP._handle_message` follows this pattern, and handlers <event handler>`. Each stanza/handler pair is then put into the
raises a ``'message'`` event event queue.
.. code-block:: python
self.event('message', msg)
#. **Process Custom Events**
The :term:`event handlers <event handler>` are then executed, passing
the stanza as the only argument.
.. note:: .. note::
Events may be raised without needing :term:`stanza objects <stanza object>`. It is possible to skip the event queue and process an event immediately
For example, you could use ``self.event('custom', {'a': 'b'})``. by using ``direct=True`` when raising the event.
You don't even need any arguments: ``self.event('no_parameters')``.
The code for :meth:`BaseXMPP._handle_message` follows this pattern, and
raises a ``'message'`` event::
self.event('message', msg)
The event call then places the message object back into the event queue
paired with an :term:`event handler`::
('event', 'message', msg_copy1, custom_event_handler_1)
('event', 'message', msg_copy2, custom_evetn_handler_2)
5. **Process Custom Events**
The stanza and :term:`event handler` are then pulled from the event
queue, and the handler is executed, passing the stanza as its only
argument. If the handler was registered as threaded, then a new thread
will be spawned for it.
.. note::
Events may be raised without needing :term:`stanza objects <stanza object>`.
For example, you could use ``self.event('custom', {'a': 'b'})``.
You don't even need any arguments: ``self.event('no_parameters')``.
However, every event handler MUST accept at least one argument. However, every event handler MUST accept at least one argument.
Finally, after a long trek, our message is handed off to the user's Finally, after a long trek, our message is handed off to the user's
custom handler in order to do awesome stuff:: custom handler in order to do awesome stuff::
reply = msg.reply() msg.reply()
reply['body'] = "Hey! This is awesome!" msg['body'] = "Hey! This is awesome!"
reply.send() msg.send()
.. index:: BaseXMPP, XMLStream .. index:: BaseXMPP, XMLStream
Raising XMPP Awareness: BaseXMPP Raising XMPP Awareness: BaseXMPP
-------------------------------- --------------------------------
While :class:`~slixmpp.xmlstream.xmlstream.XMLStream` attempts to shy away While :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream` attempts to shy away
from anything too XMPP specific, :class:`~slixmpp.basexmpp.BaseXMPP`'s from anything too XMPP specific, :class:`~sleekxmpp.basexmpp.BaseXMPP`'s
sole purpose is to provide foundational support for sending and receiving sole purpose is to provide foundational support for sending and receiving
XMPP stanzas. This support includes registering the basic message, XMPP stanzas. This support includes registering the basic message,
presence, and iq stanzas, methods for creating and sending stanzas, and presence, and iq stanzas, methods for creating and sending stanzas, and
@@ -130,14 +157,14 @@ default handlers for incoming messages and keeping track of presence
notifications. notifications.
The plugin system for adding new XEP support is also maintained by The plugin system for adding new XEP support is also maintained by
:class:`~slixmpp.basexmpp.BaseXMPP`. :class:`~sleekxmpp.basexmpp.BaseXMPP`.
.. index:: ClientXMPP, BaseXMPP .. index:: ClientXMPP, BaseXMPP
ClientXMPP ClientXMPP
---------- ----------
:class:`~slixmpp.clientxmpp.ClientXMPP` extends :class:`~sleekxmpp.clientxmpp.ClientXMPP` extends
:class:`~slixmpp.clientxmpp.BaseXMPP` with additional logic for connecting :class:`~sleekxmpp.clientxmpp.BaseXMPP` with additional logic for connecting
to an XMPP server by performing DNS lookups. It also adds support for stream to an XMPP server by performing DNS lookups. It also adds support for stream
features such as STARTTLS and SASL. features such as STARTTLS and SASL.
@@ -145,6 +172,6 @@ features such as STARTTLS and SASL.
ComponentXMPP ComponentXMPP
------------- -------------
:class:`~slixmpp.componentxmpp.ComponentXMPP` is only a thin layer on top of :class:`~sleekxmpp.componentxmpp.ComponentXMPP` is only a thin layer on top of
:class:`~slixmpp.basexmpp.BaseXMPP` that implements the component handshake :class:`~sleekxmpp.basexmpp.BaseXMPP` that implements the component handshake
protocol. protocol.

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Slixmpp documentation build configuration file, created by # SleekXMPP documentation build configuration file, created by
# sphinx-quickstart on Tue Aug 9 22:27:06 2011. # sphinx-quickstart on Tue Aug 9 22:27:06 2011.
# #
# This file is execfile()d with the current directory set to its containing dir. # This file is execfile()d with the current directory set to its containing dir.
@@ -12,17 +12,12 @@
# serve to show the default. # serve to show the default.
import sys, os import sys, os
import datetime
# If extensions (or modules to document with autodoc) are in another directory, # If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the # add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('..'))
# get version automagically from source tree
from slixmpp.version import __version__ as version
release = ".".join(version.split(".")[0:2])
# -- General configuration ----------------------------------------------------- # -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # If your documentation needs a minimal Sphinx version, state it here.
@@ -45,19 +40,17 @@ source_suffix = '.rst'
master_doc = 'index' master_doc = 'index'
# General information about the project. # General information about the project.
project = u'Slixmpp' project = u'SleekXMPP'
year = datetime.datetime.now().year copyright = u'2011, Nathan Fritz, Lance Stout'
copyright = u'{}, Nathan Fritz, Lance Stout'.format(year)
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
# built documents. # built documents.
# #
# auto imported from code!
# The short X.Y version. # The short X.Y version.
# version = '1.4' version = '1.0'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
# release = '1.4.0' release = '1.0'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
@@ -112,7 +105,7 @@ html_theme = 'haiku'
# The name for this set of Sphinx documents. If None, it defaults to # The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation". # "<project> v<release> documentation".
html_title = 'slixmpp' html_title = 'SleekXMPP'
# A shorter title for the navigation bar. Default is the same as html_title. # A shorter title for the navigation bar. Default is the same as html_title.
html_short_title = '%s Documentation' % release html_short_title = '%s Documentation' % release
@@ -175,7 +168,7 @@ html_additional_pages = {
#html_file_suffix = None #html_file_suffix = None
# Output file base name for HTML help builder. # Output file base name for HTML help builder.
htmlhelp_basename = 'Slixmppdoc' htmlhelp_basename = 'SleekXMPPdoc'
# -- Options for LaTeX output -------------------------------------------------- # -- Options for LaTeX output --------------------------------------------------
@@ -189,7 +182,7 @@ htmlhelp_basename = 'Slixmppdoc'
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]). # (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [ latex_documents = [
('index', 'Slixmpp.tex', u'Slixmpp Documentation', ('index', 'SleekXMPP.tex', u'SleekXMPP Documentation',
u'Nathan Fritz, Lance Stout', 'manual'), u'Nathan Fritz, Lance Stout', 'manual'),
] ]
@@ -222,8 +215,8 @@ latex_documents = [
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [
('index', 'slixmpp', u'Slixmpp Documentation', ('index', 'sleekxmpp', u'SleekXMPP Documentation',
[u'Nathan Fritz, Lance Stout'], 1) [u'Nathan Fritz, Lance Stout'], 1)
] ]
intersphinx_mapping = {'python': ('http://docs.python.org/3.4', 'python-objects.inv')} intersphinx_mapping = {'python': ('http://docs.python.org/3.2', 'python-objects.inv')}

View File

@@ -1,10 +1,10 @@
.. _create-plugin: .. _create-plugin:
Creating a Slixmpp Plugin Creating a SleekXMPP Plugin
=========================== ===========================
One of the goals of Slixmpp is to provide support for every draft or final One of the goals of SleekXMPP is to provide support for every draft or final
XMPP extension (`XEP <http://xmpp.org/extensions/>`_). To do this, Slixmpp has a XMPP extension (`XEP <http://xmpp.org/extensions/>`_). To do this, SleekXMPP has a
plugin mechanism for adding the functionalities required by each XEP. But even plugin mechanism for adding the functionalities required by each XEP. But even
though plugins were made to quickly implement and prototype the official XMPP though plugins were made to quickly implement and prototype the official XMPP
extensions, there is no reason you can't create your own plugin to implement extensions, there is no reason you can't create your own plugin to implement
@@ -14,11 +14,11 @@ This guide will help walk you through the steps to
implement a rudimentary version of `XEP-0077 In-band implement a rudimentary version of `XEP-0077 In-band
Registration <http://xmpp.org/extensions/xep-0077.html>`_. In-band registration Registration <http://xmpp.org/extensions/xep-0077.html>`_. In-band registration
was implemented in example 14-6 (page 223) of `XMPP: The Definitive was implemented in example 14-6 (page 223) of `XMPP: The Definitive
Guide <http://oreilly.com/catalog/9780596521271>`_ because there was no Slixmpp Guide <http://oreilly.com/catalog/9780596521271>`_ because there was no SleekXMPP
plugin for XEP-0077 at the time of writing. We will partially fix that issue plugin for XEP-0077 at the time of writing. We will partially fix that issue
here by turning the example implementation from *XMPP: The Definitive Guide* here by turning the example implementation from *XMPP: The Definitive Guide*
into a plugin. Again, note that this will not a complete implementation, and a into a plugin. Again, note that this will not a complete implementation, and a
different, more robust, official plugin for XEP-0077 may be added to Slixmpp different, more robust, official plugin for XEP-0077 may be added to SleekXMPP
in the future. in the future.
.. note:: .. note::
@@ -29,10 +29,10 @@ in the future.
First Steps First Steps
----------- -----------
Every plugin inherits from the class :mod:`BasePlugin <slixmpp.plugins.base.BasePlugin`, Every plugin inherits from the class :mod:`base_plugin <sleekxmpp.plugins.base.base_plugin>`,
and must include a ``plugin_init`` method. While the and must include a ``plugin_init`` method. While the
plugins distributed with Slixmpp must be placed in the plugins directory plugins distributed with SleekXMPP must be placed in the plugins directory
``slixmpp/plugins`` to be loaded, custom plugins may be loaded from any ``sleekxmpp/plugins`` to be loaded, custom plugins may be loaded from any
module. To do so, use the following form when registering the plugin: module. To do so, use the following form when registering the plugin:
.. code-block:: python .. code-block:: python
@@ -40,9 +40,9 @@ module. To do so, use the following form when registering the plugin:
self.register_plugin('myplugin', module=mod_containing_my_plugin) self.register_plugin('myplugin', module=mod_containing_my_plugin)
The plugin name must be the same as the plugin's class name. The plugin name must be the same as the plugin's class name.
Now, we can open our favorite text editors and create ``xep_0077.py`` in Now, we can open our favorite text editors and create ``xep_0077.py`` in
``Slixmpp/slixmpp/plugins``. We want to do some basic house-keeping and ``SleekXMPP/sleekxmpp/plugins``. We want to do some basic house-keeping and
declare the name and description of the XEP we are implementing. If you declare the name and description of the XEP we are implementing. If you
are creating your own custom plugin, you don't need to include the ``xep`` are creating your own custom plugin, you don't need to include the ``xep``
attribute. attribute.
@@ -50,15 +50,15 @@ attribute.
.. code-block:: python .. code-block:: python
""" """
Creating a Slixmpp Plugin Creating a SleekXMPP Plugin
This is a minimal implementation of XEP-0077 to serve This is a minimal implementation of XEP-0077 to serve
as a tutorial for creating Slixmpp plugins. as a tutorial for creating SleekXMPP plugins.
""" """
from slixmpp.plugins.base import BasePlugin from sleekxmpp.plugins.base import base_plugin
class xep_0077(BasePlugin): class xep_0077(base_plugin):
""" """
XEP-0077 In-Band Registration XEP-0077 In-Band Registration
""" """
@@ -68,7 +68,7 @@ attribute.
self.xep = "0077" self.xep = "0077"
Now that we have a basic plugin, we need to edit Now that we have a basic plugin, we need to edit
``slixmpp/plugins/__init__.py`` to include our new plugin by adding ``sleekxmpp/plugins/__init__.py`` to include our new plugin by adding
``'xep_0077'`` to the ``__all__`` declaration. ``'xep_0077'`` to the ``__all__`` declaration.
Interacting with Other Plugins Interacting with Other Plugins
@@ -81,20 +81,20 @@ call in a method named ``post_init`` which will be called once the plugin has
been loaded; by doing so we advertise that we can do registrations only after we been loaded; by doing so we advertise that we can do registrations only after we
finish activating the plugin. finish activating the plugin.
The ``post_init`` method needs to call ``BasePlugin.post_init(self)`` The ``post_init`` method needs to call ``base_plugin.post_init(self)``
which will mark that ``post_init`` has been called for the plugin. Once the which will mark that ``post_init`` has been called for the plugin. Once the
Slixmpp object begins processing, ``post_init`` will be called on any plugins SleekXMPP object begins processing, ``post_init`` will be called on any plugins
that have not already run ``post_init``. This allows you to register plugins and that have not already run ``post_init``. This allows you to register plugins and
their dependencies without needing to worry about the order in which you do so. their dependencies without needing to worry about the order in which you do so.
**Note:** by adding this call we have introduced a dependency on the XEP-0030 **Note:** by adding this call we have introduced a dependency on the XEP-0030
plugin. Be sure to register ``'xep_0030'`` as well as ``'xep_0077'``. Slixmpp plugin. Be sure to register ``'xep_0030'`` as well as ``'xep_0077'``. SleekXMPP
does not automatically load plugin dependencies for you. does not automatically load plugin dependencies for you.
.. code-block:: python .. code-block:: python
def post_init(self): def post_init(self):
BasePlugin.post_init(self) base_plugin.post_init(self)
self.xmpp['xep_0030'].add_feature("jabber:iq:register") self.xmpp['xep_0030'].add_feature("jabber:iq:register")
Creating Custom Stanza Objects Creating Custom Stanza Objects
@@ -141,7 +141,7 @@ behaviour:
**Note:** The accessor methods currently use title case, and not camel case. **Note:** The accessor methods currently use title case, and not camel case.
Thus if you need to access an item named ``"methodName"`` you will need to Thus if you need to access an item named ``"methodName"`` you will need to
use ``getMethodname``. This naming convention might change to full camel use ``getMethodname``. This naming convention might change to full camel
case in a future version of Slixmpp. case in a future version of SleekXMPP.
* ``sub_interfaces`` * ``sub_interfaces``
A subset of ``interfaces``, but these keys map to the text of any A subset of ``interfaces``, but these keys map to the text of any
@@ -156,14 +156,14 @@ behaviour:
.. code-block:: python .. code-block:: python
from slixmpp.xmlstream import ElementBase, ET, JID, register_stanza_plugin from sleekxmpp.xmlstream import ElementBase, ET, JID, register_stanza_plugin
from slixmpp import Iq from sleekxmpp import Iq
class Registration(ElementBase): class Registration(ElementBase):
namespace = 'jabber:iq:register' namespace = 'jabber:iq:register'
name = 'query' name = 'query'
plugin_attrib = 'register' plugin_attrib = 'register'
interfaces = {'username', 'password', 'registered', 'remove'} interfaces = set(('username', 'password', 'registered', 'remove'))
sub_interfaces = interfaces sub_interfaces = interfaces
def getRegistered(self): def getRegistered(self):
@@ -209,7 +209,7 @@ registration to our ``plugin_init`` method.
Also, we need to associate our ``Registration`` class with IQ stanzas; Also, we need to associate our ``Registration`` class with IQ stanzas;
that requires the use of the ``register_stanza_plugin`` function (in that requires the use of the ``register_stanza_plugin`` function (in
``slixmpp.xmlstream.stanzabase``) which takes the class of a parent stanza ``sleekxmpp.xmlstream.stanzabase``) which takes the class of a parent stanza
type followed by the substanza type. In our case, the parent stanza is an IQ type followed by the substanza type. In our case, the parent stanza is an IQ
stanza, and the substanza is our registration query. stanza, and the substanza is our registration query.
@@ -222,7 +222,7 @@ handler function to process registration requests.
self.description = "In-Band Registration" self.description = "In-Band Registration"
self.xep = "0077" self.xep = "0077"
self.xmpp.register_handler( self.xmpp.registerHandler(
Callback('In-Band Registration', Callback('In-Band Registration',
MatchXPath('{%s}iq/{jabber:iq:register}query' % self.xmpp.default_ns), MatchXPath('{%s}iq/{jabber:iq:register}query' % self.xmpp.default_ns),
self.__handleRegistration)) self.__handleRegistration))
@@ -347,7 +347,7 @@ method ``setForm`` which will take the names of the fields we wish to include.
# Add a blank field # Add a blank field
reg.addField(field) reg.addField(field)
iq.reply().set_payload(reg.xml) iq.reply().setPayload(reg.xml)
iq.send() iq.send()
Note how we are able to access our ``Registration`` stanza object with Note how we are able to access our ``Registration`` stanza object with
@@ -421,7 +421,7 @@ to the IQ reply.
... ...
def _sendError(self, iq, code, error_type, name, text=''): def _sendError(self, iq, code, error_type, name, text=''):
iq.reply().set_payload(iq['register'].xml) iq.reply().setPayload(iq['register'].xml)
iq.error() iq.error()
iq['error']['code'] = code iq['error']['code'] = code
iq['error']['type'] = error_type iq['error']['type'] = error_type
@@ -464,7 +464,7 @@ component examples below for how to respond to this event.
if self.backend.register(iq['from'].bare, iq['register']): if self.backend.register(iq['from'].bare, iq['register']):
# Successful registration # Successful registration
self.xmpp.event('registered_user', iq) self.xmpp.event('registered_user', iq)
iq.reply().set_payload(iq['register'].xml) iq.reply().setPayload(iq['register'].xml)
iq.send() iq.send()
else: else:
# Conflicting registration # Conflicting registration
@@ -484,15 +484,15 @@ and that we specified the form fields we wish to use with
.. code-block:: python .. code-block:: python
import slixmpp.componentxmpp import sleekxmpp.componentxmpp
class Example(slixmpp.componentxmpp.ComponentXMPP): class Example(sleekxmpp.componentxmpp.ComponentXMPP):
def __init__(self, jid, password): def __init__(self, jid, password):
slixmpp.componentxmpp.ComponentXMPP.__init__(self, jid, password, 'localhost', 8888) sleekxmpp.componentxmpp.ComponentXMPP.__init__(self, jid, password, 'localhost', 8888)
self.register_plugin('xep_0030') self.registerPlugin('xep_0030')
self.register_plugin('xep_0077') self.registerPlugin('xep_0077')
self.plugin['xep_0077'].setForm('username', 'password') self.plugin['xep_0077'].setForm('username', 'password')
self.add_event_handler("registered_user", self.reg) self.add_event_handler("registered_user", self.reg)
@@ -500,11 +500,11 @@ and that we specified the form fields we wish to use with
def reg(self, iq): def reg(self, iq):
msg = "Welcome! %s" % iq['register']['username'] msg = "Welcome! %s" % iq['register']['username']
self.send_message(iq['from'], msg, mfrom=self.fulljid) self.sendMessage(iq['from'], msg, mfrom=self.fulljid)
def unreg(self, iq): def unreg(self, iq):
msg = "Bye! %s" % iq['register']['username'] msg = "Bye! %s" % iq['register']['username']
self.send_message(iq['from'], msg, mfrom=self.fulljid) self.sendMessage(iq['from'], msg, mfrom=self.fulljid)
**Congratulations!** We now have a basic, functioning implementation of **Congratulations!** We now have a basic, functioning implementation of
XEP-0077. XEP-0077.
@@ -517,17 +517,17 @@ with some additional registration fields implemented.
.. code-block:: python .. code-block:: python
""" """
Creating a Slixmpp Plugin Creating a SleekXMPP Plugin
This is a minimal implementation of XEP-0077 to serve This is a minimal implementation of XEP-0077 to serve
as a tutorial for creating Slixmpp plugins. as a tutorial for creating SleekXMPP plugins.
""" """
from slixmpp.plugins.base import BasePlugin from sleekxmpp.plugins.base import base_plugin
from slixmpp.xmlstream.handler.callback import Callback from sleekxmpp.xmlstream.handler.callback import Callback
from slixmpp.xmlstream.matcher.xpath import MatchXPath from sleekxmpp.xmlstream.matcher.xpath import MatchXPath
from slixmpp.xmlstream import ElementBase, ET, JID, register_stanza_plugin from sleekxmpp.xmlstream import ElementBase, ET, JID, register_stanza_plugin
from slixmpp import Iq from sleekxmpp import Iq
import copy import copy
@@ -535,10 +535,10 @@ with some additional registration fields implemented.
namespace = 'jabber:iq:register' namespace = 'jabber:iq:register'
name = 'query' name = 'query'
plugin_attrib = 'register' plugin_attrib = 'register'
interfaces = {'username', 'password', 'email', 'nick', 'name', interfaces = set(('username', 'password', 'email', 'nick', 'name',
'first', 'last', 'address', 'city', 'state', 'zip', 'first', 'last', 'address', 'city', 'state', 'zip',
'phone', 'url', 'date', 'misc', 'text', 'key', 'phone', 'url', 'date', 'misc', 'text', 'key',
'registered', 'remove', 'instructions'} 'registered', 'remove', 'instructions'))
sub_interfaces = interfaces sub_interfaces = interfaces
def getRegistered(self): def getRegistered(self):
@@ -589,7 +589,7 @@ with some additional registration fields implemented.
def unregister(self, jid): def unregister(self, jid):
del self.users[jid] del self.users[jid]
class xep_0077(BasePlugin): class xep_0077(base_plugin):
""" """
XEP-0077 In-Band Registration XEP-0077 In-Band Registration
""" """
@@ -601,14 +601,14 @@ with some additional registration fields implemented.
self.form_instructions = "" self.form_instructions = ""
self.backend = UserStore() self.backend = UserStore()
self.xmpp.register_handler( self.xmpp.registerHandler(
Callback('In-Band Registration', Callback('In-Band Registration',
MatchXPath('{%s}iq/{jabber:iq:register}query' % self.xmpp.default_ns), MatchXPath('{%s}iq/{jabber:iq:register}query' % self.xmpp.default_ns),
self.__handleRegistration)) self.__handleRegistration))
register_stanza_plugin(Iq, Registration) register_stanza_plugin(Iq, Registration)
def post_init(self): def post_init(self):
BasePlugin.post_init(self) base_plugin.post_init(self)
self.xmpp['xep_0030'].add_feature("jabber:iq:register") self.xmpp['xep_0030'].add_feature("jabber:iq:register")
def __handleRegistration(self, iq): def __handleRegistration(self, iq):
@@ -634,9 +634,8 @@ with some additional registration fields implemented.
if self.backend.register(iq['from'].bare, iq['register']): if self.backend.register(iq['from'].bare, iq['register']):
# Successful registration # Successful registration
self.xmpp.event('registered_user', iq) self.xmpp.event('registered_user', iq)
reply = iq.reply() iq.reply().setPayload(iq['register'].xml)
reply.set_payload(iq['register'].xml) iq.send()
reply.send()
else: else:
# Conflicting registration # Conflicting registration
self._sendError(iq, '409', 'cancel', 'conflict', self._sendError(iq, '409', 'cancel', 'conflict',
@@ -667,16 +666,14 @@ with some additional registration fields implemented.
# Add a blank field # Add a blank field
reg.addField(field) reg.addField(field)
reply = iq.reply() iq.reply().setPayload(reg.xml)
reply.set_payload(reg.xml) iq.send()
reply.send()
def _sendError(self, iq, code, error_type, name, text=''): def _sendError(self, iq, code, error_type, name, text=''):
reply = iq.reply() iq.reply().setPayload(iq['register'].xml)
reply.set_payload(iq['register'].xml) iq.error()
reply.error() iq['error']['code'] = code
reply['error']['code'] = code iq['error']['type'] = error_type
reply['error']['type'] = error_type iq['error']['condition'] = name
reply['error']['condition'] = name iq['error']['text'] = text
reply['error']['text'] = text iq.send()
reply.send()

View File

@@ -1,48 +0,0 @@
.. _differences:
Differences from SleekXMPP
==========================
**Python 3.7+ only**
slixmpp will work on python 3.7 and above. It may work with previous
versions but we provide no guarantees.
**Stanza copies**
The same stanza object is given through all the handlers; a handler that
edits the stanza object should make its own copy.
**Replies**
Because stanzas are not copied anymore,
:meth:`Stanza.reply() <.StanzaBase.reply>` calls
(for :class:`IQs <.Iq>`, :class:`Messages <.Message>`, etc)
now return a new object instead of editing the stanza object
in-place.
**Block and threaded arguments**
All the functions that had a ``threaded=`` or ``block=`` argument
do not have it anymore. Also, :meth:`.Iq.send` **does not block
anymore**.
**Coroutine facilities**
**See** :ref:`using_asyncio`
If an event handler is a coroutine, it will be called asynchronously
in the event loop instead of inside the event caller.
A CoroutineCallback class has been added to create coroutine stream
handlers, which will be also handled in the event loop.
The :class:`~.slixmpp.stanza.Iq` objects :meth:`~.slixmpp.stanza.Iq.send`
method now **always** return a :class:`~.asyncio.Future` which result will be set
to the IQ reply when it is received, or to ``None`` if the IQ is not of
type ``get`` or ``set``.
Many plugins (WIP) calls which retrieve information also return the same
future.
**Architectural differences**
slixmpp does not have an event queue anymore, and instead processes
handlers directly after receiving the XML stanza.
.. note::
If you find something that doesnt work but should, please report it.

View File

@@ -6,33 +6,27 @@ Event Index
connected connected
- **Data:** ``{}`` - **Data:** ``{}``
- **Source:** :py:class:`~slixmpp.xmlstream.XMLstream` - **Source:** :py:class:`~sleekxmpp.clientxmpp.ClientXMPP`
Signal that a connection has been made with the XMPP server, but a session Signal that a connection has been made with the XMPP server, but a session
has not yet been established. has not yet been established.
connection_failed
- **Data:** ``{}`` or ``Failure Stanza`` if available
- **Source:** :py:class:`~slixmpp.xmlstream.XMLstream`
Signal that a connection can not be established after number of attempts.
changed_status changed_status
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.roster.item.RosterItem` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
Triggered when a presence stanza is received from a JID with a show type Triggered when a presence stanza is received from a JID with a show type
different than the last presence stanza from the same JID. different than the last presence stanza from the same JID.
changed_subscription changed_subscription
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.BaseXMPP` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
Triggered whenever a presence stanza with a type of ``subscribe``, Triggered whenever a presence stanza with a type of ``subscribe``,
``subscribed``, ``unsubscribe``, or ``unsubscribed`` is received. ``subscribed``, ``unsubscribe``, or ``unsubscribed`` is received.
Note that if the values ``xmpp.auto_authorize`` and ``xmpp.auto_subscribe`` Note that if the values ``xmpp.auto_authorize`` and ``xmpp.auto_subscribe``
are set to ``True`` or ``False``, and not ``None``, then Slixmpp will are set to ``True`` or ``False``, and not ``None``, then SleekXMPP will
either accept or reject all subscription requests before your event handlers either accept or reject all subscription requests before your event handlers
are called. Set these values to ``None`` if you wish to make more complex are called. Set these values to ``None`` if you wish to make more complex
subscription decisions. subscription decisions.
@@ -58,21 +52,21 @@ Event Index
- **Source:** - **Source:**
disco_info disco_info
- **Data:** :py:class:`~slixmpp.plugins.xep_0030.stanza.DiscoInfo` - **Data:** :py:class:`~sleekxmpp.plugins.xep_0030.stanza.DiscoInfo`
- **Source:** :py:class:`~slixmpp.plugins.xep_0030.disco.xep_0030` - **Source:** :py:class:`~sleekxmpp.plugins.xep_0030.disco.xep_0030`
Triggered whenever a ``disco#info`` result stanza is received. Triggered whenever a ``disco#info`` result stanza is received.
disco_items disco_items
- **Data:** :py:class:`~slixmpp.plugins.xep_0030.stanza.DiscoItems` - **Data:** :py:class:`~sleekxmpp.plugins.xep_0030.stanza.DiscoItems`
- **Source:** :py:class:`~slixmpp.plugins.xep_0030.disco.xep_0030` - **Source:** :py:class:`~sleekxmpp.plugins.xep_0030.disco.xep_0030`
Triggered whenever a ``disco#items`` result stanza is received. Triggered whenever a ``disco#items`` result stanza is received.
disconnected disconnected
- **Data:** ``{}`` - **Data:** ``{}``
- **Source:** :py:class:`~slixmpp.xmlstream.XMLstream` - **Source:** :py:class:`~sleekxmpp.ClientXMPP`
Signal that the connection with the XMPP server has been lost. Signal that the connection with the XMPP server has been lost.
entity_time entity_time
@@ -81,34 +75,34 @@ Event Index
failed_auth failed_auth
- **Data:** ``{}`` - **Data:** ``{}``
- **Source:** :py:class:`~slixmpp.ClientXMPP`, :py:class:`~slixmpp.plugins.xep_0078.xep_0078` - **Source:** :py:class:`~sleekxmpp.ClientXMPP`, :py:class:`~sleekxmpp.plugins.xep_0078.xep_0078`
Signal that the server has rejected the provided login credentials. Signal that the server has rejected the provided login credentials.
gmail_notify gmail_notify
- **Data:** ``{}`` - **Data:** ``{}``
- **Source:** :py:class:`~slixmpp.plugins.gmail_notify.gmail_notify` - **Source:** :py:class:`~sleekxmpp.plugins.gmail_notify.gmail_notify`
Signal that there are unread emails for the Gmail account associated with the current XMPP account. Signal that there are unread emails for the Gmail account associated with the current XMPP account.
gmail_messages gmail_messages
- **Data:** :py:class:`~slixmpp.Iq` - **Data:** :py:class:`~sleekxmpp.Iq`
- **Source:** :py:class:`~slixmpp.plugins.gmail_notify.gmail_notify` - **Source:** :py:class:`~sleekxmpp.plugins.gmail_notify.gmail_notify`
Signal that there are unread emails for the Gmail account associated with the current XMPP account. Signal that there are unread emails for the Gmail account associated with the current XMPP account.
got_online got_online
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.roster.item.RosterItem` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
If a presence stanza is received from a JID which was previously marked as If a presence stanza is received from a JID which was previously marked as
offline, and the presence has a show type of '``chat``', '``dnd``', '``away``', offline, and the presence has a show type of '``chat``', '``dnd``', '``away``',
or '``xa``', then this event is triggered as well. or '``xa``', then this event is triggered as well.
got_offline got_offline
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.roster.item.RosterItem` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
Signal that an unavailable presence stanza has been received from a JID. Signal that an unavailable presence stanza has been received from a JID.
groupchat_invite groupchat_invite
@@ -116,25 +110,25 @@ Event Index
- **Source:** - **Source:**
groupchat_direct_invite groupchat_direct_invite
- **Data:** :py:class:`~slixmpp.Message` - **Data:** :py:class:`~sleekxmpp.Message`
- **Source:** :py:class:`~slixmpp.plugins.xep_0249.direct` - **Source:** :py:class:`~sleekxmpp.plugins.xep_0249.direct`
groupchat_message groupchat_message
- **Data:** :py:class:`~slixmpp.Message` - **Data:** :py:class:`~sleekxmpp.Message`
- **Source:** :py:class:`~slixmpp.plugins.xep_0045.xep_0045` - **Source:** :py:class:`~sleekxmpp.plugins.xep_0045.xep_0045`
Triggered whenever a message is received from a multi-user chat room. Triggered whenever a message is received from a multi-user chat room.
groupchat_presence groupchat_presence
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.plugins.xep_0045.xep_0045` - **Source:** :py:class:`~sleekxmpp.plugins.xep_0045.xep_0045`
Triggered whenever a presence stanza is received from a user in a multi-user chat room. Triggered whenever a presence stanza is received from a user in a multi-user chat room.
groupchat_subject groupchat_subject
- **Data:** :py:class:`~slixmpp.Message` - **Data:** :py:class:`~sleekxmpp.Message`
- **Source:** :py:class:`~slixmpp.plugins.xep_0045.xep_0045` - **Source:** :py:class:`~sleekxmpp.plugins.xep_0045.xep_0045`
Triggered whenever the subject of a multi-user chat room is changed, or announced when joining a room. Triggered whenever the subject of a multi-user chat room is changed, or announced when joining a room.
killed killed
@@ -146,32 +140,25 @@ Event Index
- **Source:** - **Source:**
message message
- **Data:** :py:class:`~slixmpp.Message` - **Data:** :py:class:`~sleekxmpp.Message`
- **Source:** :py:class:`BaseXMPP <slixmpp.BaseXMPP>` - **Source:** :py:class:`BaseXMPP <sleekxmpp.BaseXMPP>`
Makes the contents of message stanzas available whenever one is received. Be Makes the contents of message stanzas available whenever one is received. Be
sure to check the message type in order to handle error messages. sure to check the message type in order to handle error messages.
message_error
- **Data:** :py:class:`~slixmpp.Message`
- **Source:** :py:class:`BaseXMPP <slixmpp.BaseXMPP>`
Makes the contents of message stanzas available whenever one is received.
Only handler messages with an ``error`` type.
message_form message_form
- **Data:** :py:class:`~slixmpp.plugins.xep_0004.Form` - **Data:** :py:class:`~sleekxmpp.plugins.xep_0004.Form`
- **Source:** :py:class:`~slixmpp.plugins.xep_0004.xep_0004` - **Source:** :py:class:`~sleekxmpp.plugins.xep_0004.xep_0004`
Currently the same as :term:`message_xform`. Currently the same as :term:`message_xform`.
message_xform message_xform
- **Data:** :py:class:`~slixmpp.plugins.xep_0004.Form` - **Data:** :py:class:`~sleekxmpp.plugins.xep_0004.Form`
- **Source:** :py:class:`~slixmpp.plugins.xep_0004.xep_0004` - **Source:** :py:class:`~sleekxmpp.plugins.xep_0004.xep_0004`
Triggered whenever a data form is received inside a message. Triggered whenever a data form is received inside a message.
muc::[room]::got_offline mucc::[room]::got_offline
- **Data:** - **Data:**
- **Source:** - **Source:**
@@ -188,95 +175,97 @@ Event Index
- **Source:** - **Source:**
presence_available presence_available
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.BaseXMPP` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``available``' is received. A presence stanza with a type of '``available``' is received.
presence_error presence_error
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.BaseXMPP` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``error``' is received. A presence stanza with a type of '``error``' is received.
presence_form presence_form
- **Data:** :py:class:`~slixmpp.plugins.xep_0004.Form` - **Data:** :py:class:`~sleekxmpp.plugins.xep_0004.Form`
- **Source:** :py:class:`~slixmpp.plugins.xep_0004.xep_0004` - **Source:** :py:class:`~sleekxmpp.plugins.xep_0004.xep_0004`
This event is present in the XEP-0004 plugin code, but is currently not used. This event is present in the XEP-0004 plugin code, but is currently not used.
presence_probe presence_probe
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.BaseXMPP` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``probe``' is received. A presence stanza with a type of '``probe``' is received.
presence_subscribe presence_subscribe
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.BaseXMPP` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``subscribe``' is received. A presence stanza with a type of '``subscribe``' is received.
presence_subscribed presence_subscribed
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.BaseXMPP` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``subscribed``' is received. A presence stanza with a type of '``subscribed``' is received.
presence_unavailable presence_unavailable
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.BaseXMPP` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``unavailable``' is received. A presence stanza with a type of '``unavailable``' is received.
presence_unsubscribe presence_unsubscribe
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.BaseXMPP` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``unsubscribe``' is received. A presence stanza with a type of '``unsubscribe``' is received.
presence_unsubscribed presence_unsubscribed
- **Data:** :py:class:`~slixmpp.Presence` - **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~slixmpp.BaseXMPP` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``unsubscribed``' is received. A presence stanza with a type of '``unsubscribed``' is received.
roster_update roster_update
- **Data:** :py:class:`~slixmpp.stanza.Roster` - **Data:** :py:class:`~sleekxmpp.stanza.Roster`
- **Source:** :py:class:`~slixmpp.ClientXMPP` - **Source:** :py:class:`~sleekxmpp.ClientXMPP`
An IQ result containing roster entries is received. An IQ result containing roster entries is received.
sent_presence sent_presence
- **Data:** ``{}`` - **Data:** ``{}``
- **Source:** :py:class:`~slixmpp.roster.multi.Roster` - **Source:** :py:class:`BaseXMPP <sleekxmpp.BaseXMPP>`
Signal that an initial presence stanza has been written to the XML stream. Signal that an initial presence stanza has been written to the XML stream.
session_end session_end
- **Data:** ``{}`` - **Data:** ``{}``
- **Source:** :py:class:`~slixmpp.xmlstream.XMLstream` - **Source:** :py:class:`ClientXMPP <sleekxmpp.ClientXMPP>`,
:py:class:`ComponentXMPP <sleekxmpp.ComponentXMPP>`
:py:class:`XEP-0078 <sleekxmpp.plugins.xep_0078>`
Signal that a connection to the XMPP server has been lost and the current Signal that a connection to the XMPP server has been lost and the current
stream session has ended. Currently equivalent to :term:`disconnected`, but stream session has ended. Currently equivalent to :term:`disconnected`, but
implementations of `XEP-0198: Stream Management <http://xmpp.org/extensions/xep-0198.html>`_ future implementation of `XEP-0198: Stream Management <http://xmpp.org/extensions/xep-0198.html>`_
distinguish between the two events. will distinguish the two events.
Plugins that maintain session-based state should clear themselves when Plugins that maintain session-based state should clear themselves when
this event is fired. this event is fired.
session_start session_start
- **Data:** ``{}`` - **Data:** ``{}``
- **Source:** :py:class:`ClientXMPP <slixmpp.ClientXMPP>`, - **Source:** :py:class:`ClientXMPP <sleekxmpp.ClientXMPP>`,
:py:class:`ComponentXMPP <slixmpp.ComponentXMPP>` :py:class:`ComponentXMPP <sleekxmpp.ComponentXMPP>`
:py:class:`XEP-0078 <slixmpp.plugins.xep_0078>` :py:class:`XEP-0078 <sleekxmpp.plugins.xep_0078>`
Signal that a connection to the XMPP server has been made and a session has been established. Signal that a connection to the XMPP server has been made and a session has been established.
socket_error socket_error
- **Data:** ``Socket`` exception object - **Data:** ``Socket`` exception object
- **Source:** :py:class:`~slixmpp.xmlstream.XMLstream` - **Source:** :py:class:`~sleekxmpp.xmlstream.XMLstream`
stream_error stream_error
- **Data:** :py:class:`~slixmpp.stanza.StreamError` - **Data:** :py:class:`~sleekxmpp.stanza.StreamError`
- **Source:** :py:class:`~slixmpp.BaseXMPP` - **Source:** :py:class:`~sleekxmpp.BaseXMPP`

View File

@@ -5,16 +5,24 @@ Create and Run a Server Component
================================= =================================
.. note:: .. note::
If you have any issues working through this quickstart guide If you have any issues working through this quickstart guide
join the chat room at `slixmpp@muc.poez.io or the other tutorials here, please either send a message to the
<xmpp:slixmpp@muc.poez.io?join>`_. `mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
or join the chat room at `sleek@conference.jabber.org
<xmpp:sleek@conference.jabber.org?join>`_.
If you have not yet installed Slixmpp, do so now by either checking out a version If you have not yet installed SleekXMPP, do so now by either checking out a version
with `Git <https://lab.louiz.org/poezio/slixmpp>`_. from `Github <http://github.com/fritzy/SleekXMPP>`_, or installing it using ``pip``
or ``easy_install``.
Many XMPP applications eventually graduate to requiring to run as a server .. code-block:: sh
component in order to meet scalability requirements. To demonstrate how to
pip install sleekxmpp # Or: easy_install sleekxmpp
Many XMPP applications eventually graduate to requiring to run as a server
component in order to meet scalability requirements. To demonstrate how to
turn an XMPP client bot into a component, we'll turn the echobot example turn an XMPP client bot into a component, we'll turn the echobot example
(:ref:`echobot`) into a component version. (:ref:`echobot`) into a component version.
@@ -22,7 +30,7 @@ The first difference is that we will add an additional import statement:
.. code-block:: python .. code-block:: python
from slixmpp.componentxmpp import ComponentXMPP from sleekxmpp.componentxmpp import ComponentXMPP
Likewise, we will change the bot's class definition to match: Likewise, we will change the bot's class definition to match:
@@ -40,7 +48,7 @@ a MUC component, the following could be used:
.. code-block:: python .. code-block:: python
muc = ComponentXMPP('muc.slixmpp.com', '******', 'slixmpp.com', 5555) muc = ComponentXMPP('muc.sleekxmpp.com', '******', 'sleekxmpp.com', 5555)
.. note:: .. note::
@@ -54,10 +62,10 @@ with presence.
The other, main difference with components is that the The other, main difference with components is that the
``'from'`` value for every stanza must be explicitly set, since ``'from'`` value for every stanza must be explicitly set, since
components may send stanzas from multiple JIDs. To do so, components may send stanzas from multiple JIDs. To do so,
the :meth:`~slixmpp.basexmpp.BaseXMPP.send_message()` and the :meth:`~sleekxmpp.basexmpp.BaseXMPP.send_message()` and
:meth:`~slixmpp.basexmpp.BaseXMPP.send_presence()` accept the parameters :meth:`~sleekxmpp.basexmpp.BaseXMPP.send_presence()` accept the parameters
``mfrom`` and ``pfrom``, respectively. For any method that uses ``mfrom`` and ``pfrom``, respectively. For any method that uses
:class:`~slixmpp.stanza.iq.Iq` stanzas, ``ifrom`` may be used. :class:`~sleekxmpp.stanza.iq.Iq` stanzas, ``ifrom`` may be used.
Final Product Final Product

View File

@@ -1,17 +1,25 @@
.. _echobot: .. _echobot:
=============================== ===============================
Slixmpp Quickstart - Echo Bot SleekXMPP Quickstart - Echo Bot
=============================== ===============================
.. note:: .. note::
If you have any issues working through this quickstart guide If you have any issues working through this quickstart guide
join the chat room at `slixmpp@muc.poez.io or the other tutorials here, please either send a message to the
<xmpp:slixmpp@muc.poez.io?join>`_. `mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
or join the chat room at `sleek@conference.jabber.org
<xmpp:sleek@conference.jabber.org?join>`_.
If you have not yet installed SleekXMPP, do so now by either checking out a version
from `Github <http://github.com/fritzy/SleekXMPP>`_, or installing it using ``pip``
or ``easy_install``.
.. code-block:: sh
pip install sleekxmpp # Or: easy_install sleekxmpp
If you have not yet installed Slixmpp, do so now by either checking out a version
with `Git <https://lab.louiz.org/poezio/slixmpp>`_.
As a basic starting project, we will create an echo bot which will reply to any As a basic starting project, we will create an echo bot which will reply to any
messages sent to it. We will also go through adding some basic command line configuration messages sent to it. We will also go through adding some basic command line configuration
@@ -36,12 +44,11 @@ To get started, here is a brief outline of the structure that the final project
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys import sys
import asyncio
import logging import logging
import getpass import getpass
from optparse import OptionParser from optparse import OptionParser
import slixmpp import sleekxmpp
'''Here we will create out echo bot class''' '''Here we will create out echo bot class'''
@@ -52,6 +59,24 @@ To get started, here is a brief outline of the structure that the final project
'''Finally, we connect the bot and start listening for messages''' '''Finally, we connect the bot and start listening for messages'''
Default Encoding
----------------
XMPP requires support for UTF-8 and so SleekXMPP must use UTF-8 as well. In
Python3 this is simple because Unicode is the default string type. For Python2.6+
the situation is not as easy because standard strings are simply byte arrays and
use ASCII. We can get Python to use UTF-8 as the default encoding by including:
.. code-block:: python
if sys.version_info < (3, 0):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
.. warning::
Until we are able to ensure that SleekXMPP will always use Unicode in Python2.6+, this
may cause issues embedding SleekXMPP into other applications which assume ASCII encoding.
Creating the EchoBot Class Creating the EchoBot Class
-------------------------- --------------------------
@@ -60,30 +85,30 @@ clients. Since our echo bot will only be responding to a few people, and won't n
to remember thousands of users, we will use a client connection. A client connection to remember thousands of users, we will use a client connection. A client connection
is the same type that you use with your standard IM client such as Pidgin or Psi. is the same type that you use with your standard IM client such as Pidgin or Psi.
Slixmpp comes with a :class:`ClientXMPP <slixmpp.clientxmpp.ClientXMPP>` class SleekXMPP comes with a :class:`ClientXMPP <sleekxmpp.clientxmpp.ClientXMPP>` class
which we can extend to add our message echoing feature. :class:`ClientXMPP <slixmpp.clientxmpp.ClientXMPP>` which we can extend to add our message echoing feature. :class:`ClientXMPP <sleekxmpp.clientxmpp.ClientXMPP>`
requires the parameters ``jid`` and ``password``, so we will let our ``EchoBot`` class accept those requires the parameters ``jid`` and ``password``, so we will let our ``EchoBot`` class accept those
as well. as well.
.. code-block:: python .. code-block:: python
class EchoBot(slixmpp.ClientXMPP): class EchoBot(sleekxmpp.ClientXMPP):
def __init__(self, jid, password): def __init__(self, jid, password):
super().__init__(jid, password) super(EchoBot, self).__init__(jid, password)
Handling Session Start Handling Session Start
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
The XMPP spec requires clients to broadcast its presence and retrieve its roster (buddy list) once The XMPP spec requires clients to broadcast its presence and retrieve its roster (buddy list) once
it connects and establishes a session with the XMPP server. Until these two tasks are completed, it connects and establishes a session with the XMPP server. Until these two tasks are completed,
some servers may not deliver or send messages or presence notifications to the client. So we now some servers may not deliver or send messages or presence notifications to the client. So we now
need to be sure that we retrieve our roster and send an initial presence once the session has need to be sure that we retrieve our roster and send an initial presence once the session has
started. To do that, we will register an event handler for the :term:`session_start` event. started. To do that, we will register an event handler for the :term:`session_start` event.
.. code-block:: python .. code-block:: python
def __init__(self, jid, password): def __init__(self, jid, password):
super().__init__(jid, password) super(EchoBot, self).__init__(jid, password)
self.add_event_handler('session_start', self.start) self.add_event_handler('session_start', self.start)
@@ -107,8 +132,8 @@ Our event handler, like every event handler, accepts a single parameter which ty
that was received that caused the event. In this case, ``event`` will just be an empty dictionary since that was received that caused the event. In this case, ``event`` will just be an empty dictionary since
there is no associated data. there is no associated data.
Our first task of sending an initial presence is done using :meth:`send_presence <slixmpp.basexmpp.BaseXMPP.send_presence>`. Our first task of sending an initial presence is done using :meth:`send_presence <sleekxmpp.basexmpp.BaseXMPP.send_presence>`.
Calling :meth:`send_presence <slixmpp.basexmpp.BaseXMPP.send_presence>` without any arguments will send the simplest Calling :meth:`send_presence <sleekxmpp.basexmpp.BaseXMPP.send_presence>` without any arguments will send the simplest
stanza allowed in XMPP: stanza allowed in XMPP:
.. code-block:: xml .. code-block:: xml
@@ -116,17 +141,17 @@ stanza allowed in XMPP:
<presence /> <presence />
The second requirement is fulfilled using :meth:`get_roster <slixmpp.clientxmpp.ClientXMPP.get_roster>`, which The second requirement is fulfilled using :meth:`get_roster <sleekxmpp.clientxmpp.ClientXMPP.get_roster>`, which
will send an IQ stanza requesting the roster to the server and then wait for the response. You may be wondering will send an IQ stanza requesting the roster to the server and then wait for the response. You may be wondering
what :meth:`get_roster <slixmpp.clientxmpp.ClientXMPP.get_roster>` returns since we are not saving any return what :meth:`get_roster <sleekxmpp.clientxmpp.ClientXMPP.get_roster>` returns since we are not saving any return
value. The roster data is saved by an internal handler to ``self.roster``, and in the case of a :class:`ClientXMPP value. The roster data is saved by an internal handler to ``self.roster``, and in the case of a :class:`ClientXMPP
<slixmpp.clientxmpp.ClientXMPP>` instance to ``self.client_roster``. (The difference between ``self.roster`` and <sleekxmpp.clientxmpp.ClientXMPP>` instance to ``self.client_roster``. (The difference between ``self.roster`` and
``self.client_roster`` is that ``self.roster`` supports storing roster information for multiple JIDs, which is useful ``self.client_roster`` is that ``self.roster`` supports storing roster information for multiple JIDs, which is useful
for components, whereas ``self.client_roster`` stores roster data for just the client's JID.) for components, whereas ``self.client_roster`` stores roster data for just the client's JID.)
It is possible for a timeout to occur while waiting for the server to respond, which can happen if the It is possible for a timeout to occur while waiting for the server to respond, which can happen if the
network is excessively slow or the server is no longer responding. In that case, an :class:`IQTimeout network is excessively slow or the server is no longer responding. In that case, an :class:`IQTimeout
<slixmpp.exceptions.IQTimeout>` is raised. Similarly, an :class:`IQError <slixmpp.exceptions.IQError>` exception can <sleekxmpp.exceptions.IQTimeout>` is raised. Similarly, an :class:`IQError <sleekxmpp.exceptions.IQError>` exception can
be raised if the request contained bad data or requested the roster for the wrong user. In either case, you can wrap the be raised if the request contained bad data or requested the roster for the wrong user. In either case, you can wrap the
``get_roster()`` call in a ``try``/``except`` block to retry the roster retrieval process. ``get_roster()`` call in a ``try``/``except`` block to retry the roster retrieval process.
@@ -153,7 +178,7 @@ whenever a messsage is received.
.. code-block:: python .. code-block:: python
def __init__(self, jid, password): def __init__(self, jid, password):
super().__init__(jid, password) super(EchoBot, self).__init__(jid, password)
self.add_event_handler('session_start', self.start) self.add_event_handler('session_start', self.start)
self.add_event_handler('message', self.message) self.add_event_handler('message', self.message)
@@ -173,10 +198,10 @@ or ``chat``. (Other potential types are ``error``, ``headline``, and ``groupchat
Let's take a closer look at the ``.reply()`` method used above. For message stanzas, Let's take a closer look at the ``.reply()`` method used above. For message stanzas,
``.reply()`` accepts the parameter ``body`` (also as the first positional argument), ``.reply()`` accepts the parameter ``body`` (also as the first positional argument),
which is then used as the value of the ``<body />`` element of the message. which is then used as the value of the ``<body />`` element of the message.
Setting the appropriate ``to`` JID is also handled by ``.reply()``. Setting the appropriate ``to`` JID is also handled by ``.reply()``.
Another way to have sent the reply message would be to use :meth:`send_message <slixmpp.basexmpp.BaseXMPP.send_message>`, Another way to have sent the reply message would be to use :meth:`send_message <sleekxmpp.basexmpp.BaseXMPP.send_message>`,
which is a convenience method for generating and sending a message based on the values passed to it. If we were to use which is a convenience method for generating and sending a message based on the values passed to it. If we were to use
this method, the above code would look as so: this method, the above code would look as so:
@@ -204,20 +229,20 @@ Whichever method you choose to use, the results in action will look like this:
XMPP does not require stanzas sent by a client to include a ``from`` attribute, and XMPP does not require stanzas sent by a client to include a ``from`` attribute, and
leaves that responsibility to the XMPP server. However, if a sent stanza does leaves that responsibility to the XMPP server. However, if a sent stanza does
include a ``from`` attribute, it must match the full JID of the client or some include a ``from`` attribute, it must match the full JID of the client or some
servers will reject it. Slixmpp thus leaves out the ``from`` attribute when replying servers will reject it. SleekXMPP thus leaves out the ``from`` attribute when replying
using a client connection. using a client connection.
Command Line Arguments and Logging Command Line Arguments and Logging
---------------------------------- ----------------------------------
While this isn't part of Slixmpp itself, we do want our echo bot program to be able While this isn't part of SleekXMPP itself, we do want our echo bot program to be able
to accept a JID and password from the command line instead of hard coding them. We will to accept a JID and password from the command line instead of hard coding them. We will
use the ``optparse`` module for this, though there are several alternative methods, including use the ``optparse`` module for this, though there are several alternative methods, including
the newer ``argparse`` module. the newer ``argparse`` module.
We want to accept three parameters: the JID for the echo bot, its password, and a flag for We want to accept three parameters: the JID for the echo bot, its password, and a flag for
displaying the debugging logs. We also want these to be optional parameters, since passing displaying the debugging logs. We also want these to be optional parameters, since passing
a password directly through the command line can be a security risk. a password directly through the command line can be a security risk.
.. code-block:: python .. code-block:: python
@@ -278,21 +303,21 @@ the ``EchoBot.__init__`` method instead.
.. note:: .. note::
If you are using the OpenFire server, you will need to include an additional If you are using the OpenFire server, you will need to include an additional
configuration step. OpenFire supports a different version of SSL than what configuration step. OpenFire supports a different version of SSL than what
most servers and Slixmpp support. most servers and SleekXMPP support.
.. code-block:: python .. code-block:: python
import ssl import ssl
xmpp.ssl_version = ssl.PROTOCOL_SSLv3 xmpp.ssl_version = ssl.PROTOCOL_SSLv3
Now we're ready to connect and begin echoing messages. If you have the package Now we're ready to connect and begin echoing messages. If you have the package
``aiodns`` installed, then the :meth:`slixmpp.clientxmpp.ClientXMPP` method ``dnspython`` installed, then the :meth:`sleekxmpp.clientxmpp.ClientXMPP` method
will perform a DNS query to find the appropriate server to connect to for the will perform a DNS query to find the appropriate server to connect to for the
given JID. If you do not have ``aiodns``, then Slixmpp will attempt to given JID. If you do not have ``dnspython``, then SleekXMPP will attempt to
connect to the hostname used by the JID, unless an address tuple is supplied connect to the hostname used by the JID, unless an address tuple is supplied
to :meth:`slixmpp.clientxmpp.ClientXMPP`. to :meth:`sleekxmpp.clientxmpp.ClientXMPP`.
.. code-block:: python .. code-block:: python
@@ -305,19 +330,35 @@ to :meth:`slixmpp.clientxmpp.ClientXMPP`.
else: else:
print('Unable to connect') print('Unable to connect')
To begin responding to messages, you'll see we called :meth:`slixmpp.basexmpp.BaseXMPP.process`
which will start the event handling, send queue, and XML reader threads. It will also call
the :meth:`slixmpp.plugins.base.BasePlugin.post_init` method on all registered plugins. By
passing ``block=True`` to :meth:`slixmpp.basexmpp.BaseXMPP.process` we are running the
main processing loop in the main thread of execution. The :meth:`slixmpp.basexmpp.BaseXMPP.process`
call will not return until after Slixmpp disconnects. If you need to run the client in the background
for another program, use ``block=False`` to spawn the processing loop in its own thread.
.. note:: .. note::
Before 1.0, controlling the blocking behaviour of :meth:`slixmpp.basexmpp.BaseXMPP.process` was For Google Talk users withouth ``dnspython`` installed, the above code
should look like:
.. code-block:: python
if __name__ == '__main__':
# .. option parsing & echo bot configuration
if xmpp.connect(('talk.google.com', 5222)):
xmpp.process(block=True)
else:
print('Unable to connect')
To begin responding to messages, you'll see we called :meth:`sleekxmpp.basexmpp.BaseXMPP.process`
which will start the event handling, send queue, and XML reader threads. It will also call
the :meth:`sleekxmpp.plugins.base.base_plugin.post_init` method on all registered plugins. By
passing ``block=True`` to :meth:`sleekxmpp.basexmpp.BaseXMPP.process` we are running the
main processing loop in the main thread of execution. The :meth:`sleekxmpp.basexmpp.BaseXMPP.process`
call will not return until after SleekXMPP disconnects. If you need to run the client in the background
for another program, use ``block=False`` to spawn the processing loop in its own thread.
.. note::
Before 1.0, controlling the blocking behaviour of :meth:`sleekxmpp.basexmpp.BaseXMPP.process` was
done via the ``threaded`` argument. This arrangement was a source of confusion because some users done via the ``threaded`` argument. This arrangement was a source of confusion because some users
interpreted that as controlling whether or not Slixmpp used threads at all, instead of how interpreted that as controlling whether or not SleekXMPP used threads at all, instead of how
the processing loop itself was spawned. the processing loop itself was spawned.
The statements ``xmpp.process(threaded=False)`` and ``xmpp.process(block=True)`` are equivalent. The statements ``xmpp.process(threaded=False)`` and ``xmpp.process(block=True)`` are equivalent.
@@ -329,7 +370,7 @@ The Final Product
----------------- -----------------
Here then is what the final result should look like after working through the guide above. The code Here then is what the final result should look like after working through the guide above. The code
can also be found in the Slixmpp `examples directory <https://lab.louiz.org/poezio/slixmpp/tree/master/examples>`_. can also be found in the SleekXMPP `examples directory <http://github.com/fritzy/SleekXMPP/tree/master/examples>`_.
.. compound:: .. compound::

View File

@@ -1,17 +1,17 @@
Send/Receive IQ Stanzas Send/Receive IQ Stanzas
======================= =======================
Unlike :class:`~slixmpp.stanza.message.Message` and Unlike :class:`~sleekxmpp.stanza.message.Message` and
:class:`~slixmpp.stanza.presence.Presence` stanzas which only use :class:`~sleekxmpp.stanza.presence.Presence` stanzas which only use
text data for basic usage, :class:`~slixmpp.stanza.iq.Iq` stanzas text data for basic usage, :class:`~sleekxmpp.stanza.iq.Iq` stanzas
require using XML payloads, and generally entail creating a new require using XML payloads, and generally entail creating a new
Slixmpp plugin to provide the necessary convenience methods to SleekXMPP plugin to provide the necessary convenience methods to
make working with them easier. make working with them easier.
Basic Use Basic Use
--------- ---------
XMPP's use of :class:`~slixmpp.stanza.iq.Iq` stanzas is built around XMPP's use of :class:`~sleekxmpp.stanza.iq.Iq` stanzas is built around
namespaced ``<query />`` elements. For clients, just sending the namespaced ``<query />`` elements. For clients, just sending the
empty ``<query />`` element will suffice for retrieving information. For empty ``<query />`` element will suffice for retrieving information. For
example, a very basic implementation of service discovery would just example, a very basic implementation of service discovery would just
@@ -26,18 +26,18 @@ need to be able to send:
Creating Iq Stanzas Creating Iq Stanzas
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
Slixmpp provides built-in support for creating basic :class:`~slixmpp.stanza.iq.Iq` SleekXMPP provides built-in support for creating basic :class:`~sleekxmpp.stanza.iq.Iq`
stanzas this way. The relevant methods are: stanzas this way. The relevant methods are:
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq` * :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq`
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_get` * :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_get`
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_set` * :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_set`
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_result` * :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_result`
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_error` * :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_error`
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_query` * :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_query`
These methods all follow the same pattern: create or modify an existing These methods all follow the same pattern: create or modify an existing
:class:`~slixmpp.stanza.iq.Iq` stanza, set the ``'type'`` value based :class:`~sleekxmpp.stanza.iq.Iq` stanza, set the ``'type'`` value based
on the method name, and finally add a ``<query />`` element with the given on the method name, and finally add a ``<query />`` element with the given
namespace. For example, to produce the query above, you would use: namespace. For example, to produce the query above, you would use:
@@ -50,14 +50,14 @@ namespace. For example, to produce the query above, you would use:
Sending Iq Stanzas Sending Iq Stanzas
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
Once an :class:`~slixmpp.stanza.iq.Iq` stanza is created, sending it Once an :class:`~sleekxmpp.stanza.iq.Iq` stanza is created, sending it
over the wire is done using its :meth:`~slixmpp.stanza.iq.Iq.send()` over the wire is done using its :meth:`~sleekxmpp.stanza.iq.Iq.send()`
method, like any other stanza object. However, there are a few extra method, like any other stanza object. However, there are a few extra
options to control how to wait for the query's response. options to control how to wait for the query's response.
These options are: These options are:
* ``block``: The default behaviour is that :meth:`~slixmpp.stanza.iq.Iq.send()` * ``block``: The default behaviour is that :meth:`~sleekxmpp.stanza.iq.Iq.send()`
will block until a response is received and the response stanza will be the will block until a response is received and the response stanza will be the
return value. Setting ``block`` to ``False`` will cause the call to return return value. Setting ``block`` to ``False`` will cause the call to return
immediately. In which case, you will need to arrange some way to capture immediately. In which case, you will need to arrange some way to capture
@@ -74,7 +74,7 @@ These options are:
To change the timeout for a single call, the ``timeout`` parameter works: To change the timeout for a single call, the ``timeout`` parameter works:
.. code-block:: python .. code-block:: python
iq.send(timeout=60) iq.send(timeout=60)
* ``callback``: When not using a blocking call, using the ``callback`` * ``callback``: When not using a blocking call, using the ``callback``
@@ -85,16 +85,16 @@ These options are:
.. code-block:: python .. code-block:: python
cb_name = iq.send(callback=self.a_callback) cb_name = iq.send(callback=self.a_callback)
# ... later if we need to cancel # ... later if we need to cancel
self.remove_handler(cb_name) self.remove_handler(cb_name)
Properly working with :class:`~slixmpp.stanza.iq.Iq` stanzas requires Properly working with :class:`~sleekxmpp.stanza.iq.Iq` stanzas requires
handling the intended, normal flow, error responses, and timed out handling the intended, normal flow, error responses, and timed out
requests. To make this easier, two exceptions may be thrown by requests. To make this easier, two exceptions may be thrown by
:meth:`~slixmpp.stanza.iq.Iq.send()`: :exc:`~slixmpp.exceptions.IqError` :meth:`~sleekxmpp.stanza.iq.Iq.send()`: :exc:`~sleekxmpp.exceptions.IqError`
and :exc:`~slixmpp.exceptions.IqTimeout`. These exceptions only and :exc:`~sleekxmpp.exceptions.IqTimeout`. These exceptions only
apply to the default, blocking calls. apply to the default, blocking calls.
.. code-block:: python .. code-block:: python
@@ -110,7 +110,7 @@ apply to the default, blocking calls.
pass pass
If you do not care to distinguish between errors and timeouts, then you If you do not care to distinguish between errors and timeouts, then you
can combine both cases with a generic :exc:`~slixmpp.exceptions.XMPPError` can combine both cases with a generic :exc:`~sleekxmpp.exceptions.XMPPError`
exception: exception:
.. code-block:: python .. code-block:: python
@@ -124,24 +124,24 @@ exception:
Advanced Use Advanced Use
------------ ------------
Going beyond the basics provided by Slixmpp requires building at least a Going beyond the basics provided by SleekXMPP requires building at least a
rudimentary Slixmpp plugin to create a :term:`stanza object` for rudimentary SleekXMPP plugin to create a :term:`stanza object` for
interfacting with the :class:`~slixmpp.stanza.iq.Iq` payload. interfacting with the :class:`~sleekxmpp.stanza.iq.Iq` payload.
.. seealso:: .. seealso::
* :ref:`create-plugin` * :ref:`create-plugin`
* :ref:`work-with-stanzas` * :ref:`work-with-stanzas`
* :ref:`using-handlers-matchers` * :ref:`using-handlers-matchers`
The typical way to respond to :class:`~sleekxmpp.stanza.iq.Iq` requests is
The typical way to respond to :class:`~slixmpp.stanza.iq.Iq` requests is
to register stream handlers. As an example, suppose we create a stanza class to register stream handlers. As an example, suppose we create a stanza class
named ``CustomXEP`` which uses the XML element ``<query xmlns="custom-xep" />``, named ``CustomXEP`` which uses the XML element ``<query xmlns="custom-xep" />``,
and has a :attr:`~slixmpp.xmlstream.stanzabase.ElementBase.plugin_attrib` value and has a :attr:`~sleekxmpp.xmlstream.stanzabase.ElementBase.plugin_attrib` value
of ``custom_xep``. of ``custom_xep``.
There are two types of incoming :class:`~slixmpp.stanza.iq.Iq` requests: There are two types of incoming :class:`~sleekxmpp.stanza.iq.Iq` requests:
``get`` and ``set``. You can register a handler that will accept both and then ``get`` and ``set``. You can register a handler that will accept both and then
filter by type as needed, as so: filter by type as needed, as so:
@@ -167,7 +167,7 @@ filter by type as needed, as so:
If you want to filter out query types beforehand, you can adjust the matching If you want to filter out query types beforehand, you can adjust the matching
filter by using ``@type=get`` or ``@type=set`` if you are using the recommended filter by using ``@type=get`` or ``@type=set`` if you are using the recommended
:class:`~slixmpp.xmlstream.matcher.stanzapath.StanzaPath` matcher. :class:`~sleekxmpp.xmlstream.matcher.stanzapath.StanzaPath` matcher.
.. code-block:: python .. code-block:: python

View File

@@ -1,19 +1,27 @@
.. _mucbot: .. _mucbot:
========================= =========================
Multi-User Chat (MUC) Bot Mulit-User Chat (MUC) Bot
========================= =========================
.. note:: .. note::
If you have any issues working through this quickstart guide If you have any issues working through this quickstart guide
join the chat room at `slixmpp@muc.poez.io or the other tutorials here, please either send a message to the
<xmpp:slixmpp@muc.poez.io?join>`_. `mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
or join the chat room at `sleek@conference.jabber.org
<xmpp:sleek@conference.jabber.org?join>`_.
If you have not yet installed Slixmpp, do so now by either checking out a version If you have not yet installed SleekXMPP, do so now by either checking out a version
from `Git <https://lab.louiz.org/poezio/slixmpp>`_. from `Github <http://github.com/fritzy/SleekXMPP>`_, or installing it using ``pip``
or ``easy_install``.
Now that you've got the basic gist of using Slixmpp by following the .. code-block:: sh
pip install sleekxmpp # Or: easy_install sleekxmpp
Now that you've got the basic gist of using SleekXMPP by following the
echobot example (:ref:`echobot`), we can use one of the bundled plugins echobot example (:ref:`echobot`), we can use one of the bundled plugins
to create a very popular XMPP starter project: a `Multi-User Chat`_ to create a very popular XMPP starter project: a `Multi-User Chat`_
(MUC) bot. Our bot will login to an XMPP server, join an MUC chat room (MUC) bot. Our bot will login to an XMPP server, join an MUC chat room
@@ -28,7 +36,7 @@ Joining The Room
As usual, our code will be based on the pattern explained in :ref:`echobot`. As usual, our code will be based on the pattern explained in :ref:`echobot`.
To start, we create an ``MUCBot`` class based on To start, we create an ``MUCBot`` class based on
:class:`ClientXMPP <slixmpp.clientxmpp.ClientXMPP>` and which accepts :class:`ClientXMPP <sleekxmpp.clientxmpp.ClientXMPP>` and which accepts
parameters for the JID of the MUC room to join, and the nick that the parameters for the JID of the MUC room to join, and the nick that the
bot will use inside the chat room. We also register an bot will use inside the chat room. We also register an
:term:`event handler` for the :term:`session_start` event. :term:`event handler` for the :term:`session_start` event.
@@ -36,12 +44,12 @@ bot will use inside the chat room. We also register an
.. code-block:: python .. code-block:: python
import slixmpp import sleekxmpp
class MUCBot(slixmpp.ClientXMPP): class MUCBot(sleekxmpp.ClientXMPP):
def __init__(self, jid, password, room, nick): def __init__(self, jid, password, room, nick):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.room = room self.room = room
self.nick = nick self.nick = nick
@@ -63,17 +71,17 @@ has been established:
def start(self, event): def start(self, event):
self.get_roster() self.get_roster()
self.send_presence() self.send_presence()
self.plugin['xep_0045'].join_muc(self.room, self.plugin['xep_0045'].joinMUC(self.room,
self.nick, self.nick,
wait=True) wait=True)
Note that as in :ref:`echobot`, we need to include send an initial presence and request Note that as in :ref:`echobot`, we need to include send an initial presence and request
the roster. Next, we want to join the group chat, so we call the the roster. Next, we want to join the group chat, so we call the
``join_muc`` method of the MUC plugin. ``joinMUC`` method of the MUC plugin.
.. note:: .. note::
The :attr:`plugin <slixmpp.basexmpp.BaseXMPP.plugin>` attribute is The :attr:`plugin <sleekxmpp.basexmpp.BaseXMPP.plugin>` attribute is
dictionary that maps to instances of plugins that we have previously dictionary that maps to instances of plugins that we have previously
registered, by their names. registered, by their names.
@@ -107,7 +115,7 @@ event inside the bot's ``__init__`` function.
.. code-block:: python .. code-block:: python
def __init__(self, jid, password, room, nick): def __init__(self, jid, password, room, nick):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.room = room self.room = room
self.nick = nick self.nick = nick
@@ -151,7 +159,7 @@ event so it's a good idea to register an event handler for it.
.. code-block:: python .. code-block:: python
def __init__(self, jid, password, room, nick): def __init__(self, jid, password, room, nick):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.room = room self.room = room
self.nick = nick self.nick = nick

View File

@@ -5,17 +5,19 @@ Enable HTTP Proxy Support
========================= =========================
.. note:: .. note::
If you have any issues working through this quickstart guide If you have any issues working through this quickstart guide
join the chat room at `slixmpp@muc.poez.io or the other tutorials here, please either send a message to the
<xmpp:slixmpp@muc.poez.io?join>`_. `mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
or join the chat room at `sleek@conference.jabber.org
<xmpp:sleek@conference.jabber.org?join>`_.
In some instances, you may wish to route XMPP traffic through In some instances, you may wish to route XMPP traffic through
an HTTP proxy, probably to get around restrictive firewalls. an HTTP proxy, probably to get around restrictive firewalls.
Slixmpp provides support for basic HTTP proxying with DIGEST SleekXMPP provides support for basic HTTP proxying with DIGEST
authentication. authentication.
Enabling proxy support is done in two steps. The first is to instruct Slixmpp Enabling proxy support is done in two steps. The first is to instruct SleekXMPP
to use a proxy, and the second is to configure the proxy details: to use a proxy, and the second is to configure the proxy details:
.. code-block:: python .. code-block:: python

View File

@@ -2,29 +2,31 @@ Sign in, Send a Message, and Disconnect
======================================= =======================================
.. note:: .. note::
If you have any issues working through this quickstart guide If you have any issues working through this quickstart guide
join the chat room at `slixmpp@muc.poez.io or the other tutorials here, please either send a message to the
<xmpp:slixmpp@muc.poez.io?join>`_. `mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
or join the chat room at `sleek@conference.jabber.org
<xmpp:sleek@conference.jabber.org?join>`_.
A common use case for Slixmpp is to send one-off messages from A common use case for SleekXMPP is to send one-off messages from
time to time. For example, one use case could be sending out a notice when time to time. For example, one use case could be sending out a notice when
a shell script finishes a task. a shell script finishes a task.
We will create our one-shot bot based on the pattern explained in :ref:`echobot`. To We will create our one-shot bot based on the pattern explained in :ref:`echobot`. To
start, we create a client class based on :class:`ClientXMPP <slixmpp.clientxmpp.ClientXMPP>` and start, we create a client class based on :class:`ClientXMPP <sleekxmpp.clientxmpp.ClientXMPP>` and
register a handler for the :term:`session_start` event. We will also accept parameters register a handler for the :term:`session_start` event. We will also accept parameters
for the JID that will receive our message, and the string content of the message. for the JID that will receive our message, and the string content of the message.
.. code-block:: python .. code-block:: python
import slixmpp import sleekxmpp
class SendMsgBot(slixmpp.ClientXMPP): class SendMsgBot(sleekxmpp.ClientXMPP):
def __init__(self, jid, password, recipient, msg): def __init__(self, jid, password, recipient, msg):
super().__init__(jid, password) super(SendMsgBot, self).__init__(jid, password)
self.recipient = recipient self.recipient = recipient
self.msg = msg self.msg = msg
@@ -36,7 +38,7 @@ for the JID that will receive our message, and the string content of the message
self.get_roster() self.get_roster()
Note that as in :ref:`echobot`, we need to include send an initial presence and request Note that as in :ref:`echobot`, we need to include send an initial presence and request
the roster. Next, we want to send our message, and to do that we will use :meth:`send_message <slixmpp.basexmpp.BaseXMPP.send_message>`. the roster. Next, we want to send our message, and to do that we will use :meth:`send_message <sleekxmpp.basexmpp.BaseXMPP.send_message>`.
.. code-block:: python .. code-block:: python
@@ -46,12 +48,12 @@ the roster. Next, we want to send our message, and to do that we will use :meth:
self.send_message(mto=self.recipient, mbody=self.msg) self.send_message(mto=self.recipient, mbody=self.msg)
Finally, we need to disconnect the client using :meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>`. Finally, we need to disconnect the client using :meth:`disconnect <sleekxmpp.xmlstream.XMLStream.disconnect>`.
Now, sent stanzas are placed in a queue to pass them to the send thread. Now, sent stanzas are placed in a queue to pass them to the send thread. If we were to call
:meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>` by default will wait for an :meth:`disconnect <sleekxmpp.xmlstream.XMLStream.disconnect>` without any parameters, then it is possible
acknowledgement from the server for at least `2.0` seconds. This time is configurable with for the client to disconnect before the send queue is processed and the message is actually
the `wait` parameter. If `0.0` is passed for `wait`, :meth:`disconnect sent on the wire. To ensure that our message is processed, we use
<slixmpp.xmlstream.XMLStream.disconnect>` will not close the connection gracefully. :meth:`disconnect(wait=True) <sleekxmpp.xmlstream.XMLStream.disconnect>`.
.. code-block:: python .. code-block:: python
@@ -61,12 +63,12 @@ the `wait` parameter. If `0.0` is passed for `wait`, :meth:`disconnect
self.send_message(mto=self.recipient, mbody=self.msg) self.send_message(mto=self.recipient, mbody=self.msg)
self.disconnect() self.disconnect(wait=True)
.. warning:: .. warning::
If you happen to be adding stanzas to the send queue faster than the send thread If you happen to be adding stanzas to the send queue faster than the send thread
can process them, then :meth:`disconnect() <slixmpp.xmlstream.XMLStream.disconnect>` can process them, then :meth:`disconnect(wait=True) <sleekxmpp.xmlstream.XMLStream.disconnect>`
will block and not disconnect. will block and not disconnect.
Final Product Final Product

View File

@@ -9,20 +9,21 @@ Glossary
stream handler stream handler
A callback function that accepts stanza objects pulled directly A callback function that accepts stanza objects pulled directly
from the XML stream. A stream handler is encapsulated in a from the XML stream. A stream handler is encapsulated in a
object that includes a :class:`Matcher <.MatcherBase>` object, and object that includes a :term:`Matcher` object, and which provides
which provides additional semantics. For example, the additional semantics. For example, the ``Waiter`` handler wrapper
:class:`.Waiter` handler wrapper blocks thread execution until a blocks thread execution until a matching stanza is received.
matching stanza is received.
event handler event handler
A callback function that responds to events raised by A callback function that responds to events raised by
:meth:`.XMLStream.event`. ``XMLStream.event``. An event handler may be marked as
threaded, allowing it to execute outside of the main processing
loop.
stanza object stanza object
Informally may refer both to classes which extend :class:`.ElementBase` Informally may refer both to classes which extend ``ElementBase``
or :class:`.StanzaBase`, and to objects of such classes. or ``StanzaBase``, and to objects of such classes.
A stanza object is a wrapper for an XML object which exposes :class:`dict` A stanza object is a wrapper for an XML object which exposes ``dict``
like interfaces which may be assigned to, read from, or deleted. like interfaces which may be assigned to, read from, or deleted.
stanza plugin stanza plugin

View File

@@ -18,7 +18,7 @@ Working with service discovery is about creating and querying these nodes.
According to XEP-0030, a node may contain three types of information: According to XEP-0030, a node may contain three types of information:
identities, features, and items. (Further, extensible, information types are identities, features, and items. (Further, extensible, information types are
defined in `XEP-0128 <http://xmpp.org/extensions/xep-0128.html>`_, but they are defined in `XEP-0128 <http://xmpp.org/extensions/xep-0128.html>`_, but they are
not yet implemented by Slixmpp.) Slixmpp provides methods to configure each not yet implemented by SleekXMPP.) SleekXMPP provides methods to configure each
of these node attributes. of these node attributes.
Configuring Service Discovery Configuring Service Discovery
@@ -61,7 +61,7 @@ operation using these stanzas without doing any complex operations such as
checking an ACL, etc. checking an ACL, etc.
You may find it necessary at some point to revert a particular node or JID to You may find it necessary at some point to revert a particular node or JID to
using the default, static handlers. To do so, use the method ``restore_defaults()``. using the default, static handlers. To do so, use the method ``make_static()``.
You may also elect to only convert a given set of actions instead. You may also elect to only convert a given set of actions instead.
Creating a Node Handler Creating a Node Handler
@@ -119,7 +119,7 @@ the same order as expected using positional arguments.
xmpp['xep_0030'].add_identity(category='client', xmpp['xep_0030'].add_identity(category='client',
itype='bot', itype='bot',
name='Slixmpp', name='Sleek',
node='foo', node='foo',
jid=xmpp.boundjid.full, jid=xmpp.boundjid.full,
lang='no') lang='no')
@@ -159,10 +159,10 @@ item itself, and the JID and node that will own the item.
.. note:: .. note::
In this case, the owning JID and node are provided with the In this case, the owning JID and node are provided with the
parameters ``ijid`` and ``node``. parameters ``ijid`` and ``node``.
Performing Disco Queries Peforming Disco Queries
------------------------ -----------------------
The methods ``get_info()`` and ``get_items()`` are used to query remote JIDs The methods ``get_info()`` and ``get_items()`` are used to query remote JIDs
and their nodes for disco information. Since these methods are wrappers for and their nodes for disco information. Since these methods are wrappers for
sending Iq stanzas, they also accept all of the parameters of the ``Iq.send()`` sending Iq stanzas, they also accept all of the parameters of the ``Iq.send()``
@@ -172,10 +172,11 @@ the `XEP-0059 <http://xmpp.org/extensions/xep-0059.html>`_ plug-in.
.. code-block:: python .. code-block:: python
info = yield from self['xep_0030'].get_info(jid='foo@example.com', info = self['xep_0030'].get_info(jid='foo@example.com',
node='bar', node='bar',
ifrom='baz@mycomponent.example.com', ifrom='baz@mycomponent.example.com',
timeout=30) block=True,
timeout=30)
items = self['xep_0030'].get_info(jid='foo@example.com', items = self['xep_0030'].get_info(jid='foo@example.com',
node='bar', node='bar',
@@ -196,5 +197,5 @@ a full Iq stanza.
info = self['xep_0030'].get_info(node='foo', local=True) info = self['xep_0030'].get_info(node='foo', local=True)
items = self['xep_0030'].get_items(jid='somejid@mycomponent.example.com', items = self['xep_0030'].get_items(jid='somejid@mycomponent.example.com',
node='bar', node='bar',
local=True) local=True)

View File

@@ -1,36 +1,49 @@
Slixmpp SleekXMPP
######### #########
.. sidebar:: Get the Code .. sidebar:: Get the Code
The latest source code for Slixmpp may be found on the `Git repo .. code-block:: sh
<https://lab.louiz.org/poezio/slixmpp>`_. ::
git clone https://lab.louiz.org/poezio/slixmpp pip install sleekxmpp
An XMPP chat room is available for discussing and getting help with slixmpp. The latest source code for SleekXMPP may be found on `Github
<http://github.com/fritzy/SleekXMPP>`_. Releases can be found in the
``master`` branch, while the latest development version is in the
``develop`` branch.
**Latest Stable Release**
- `1.0 <http://github.com/fritzy/SleekXMPP/zipball/1.0>`_
**Develop Releases**
- `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_
A mailing list and XMPP chat room are available for discussing and getting
help with SleekXMPP.
**Mailing List**
`SleekXMPP Discussion on Google Groups <http://groups.google.com/group/sleekxmpp-discussion>`_
**Chat** **Chat**
`slixmpp@muc.poez.io <xmpp:slixmpp@muc.poez.io?join>`_ `sleek@conference.jabber.org <xmpp:sleek@conference.jabber.org?join>`_
**Reporting bugs**
You can report bugs at http://lab.louiz.org/poezio/slixmpp/issues.
.. note:: SleekXMPP is an :ref:`MIT licensed <license>` XMPP library for Python 2.6/3.1+,
slixmpp is a friendly fork of `SleekXMPP <https://github.com/fritzy/SleekXMPP>`_ and is featured in examples in
which goal is to use asyncio instead of threads to handle networking. See `XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271>`_
:ref:`differences`. by Kevin Smith, Remko Tronçon, and Peter Saint-Andre. If you've arrived
here from reading the Definitive Guide, please see the notes on updating
the examples to the latest version of SleekXMPP.
Slixmpp is an :ref:`MIT licensed <license>` XMPP library for Python 3.7+, SleekXMPP's design goals and philosphy are:
Slixmpp's design goals and philosphy are:
**Low number of dependencies** **Low number of dependencies**
Installing and using Slixmpp should be as simple as possible, without Installing and using SleekXMPP should be as simple as possible, without
having to deal with long dependency chains. having to deal with long dependency chains.
As part of reducing the number of dependencies, some third party As part of reducing the number of dependencies, some third party
modules are included with Slixmpp in the ``thirdparty`` directory. modules are included with SleekXMPP in the ``thirdparty`` directory.
Imports from this module first try to import an existing installed Imports from this module first try to import an existing installed
version before loading the packaged version, when possible. version before loading the packaged version, when possible.
@@ -42,20 +55,19 @@ Slixmpp's design goals and philosphy are:
XEPs. XEPs.
**Rewarding to work with** **Rewarding to work with**
As much as possible, Slixmpp should allow things to "just work" using As much as possible, SleekXMPP should allow things to "just work" using
sensible defaults and appropriate abstractions. XML can be ugly to work sensible defaults and appropriate abstractions. XML can be ugly to work
with, but it doesn't have to be that way. with, but it doesn't have to be that way.
Here's your first SleekXMPP Bot:
Here's your first Slixmpp Bot:
-------------------------------- --------------------------------
.. code-block:: python .. code-block:: python
import asyncio
import logging import logging
from slixmpp import ClientXMPP from sleekxmpp import ClientXMPP
from sleekxmpp.exceptions import IqError, IqTimeout
class EchoBot(ClientXMPP): class EchoBot(ClientXMPP):
@@ -73,13 +85,27 @@ Here's your first Slixmpp Bot:
# Here's how to access plugins once you've registered them: # Here's how to access plugins once you've registered them:
# self['xep_0030'].add_feature('echo_demo') # self['xep_0030'].add_feature('echo_demo')
# If you are working with an OpenFire server, you will
# need to use a different SSL version:
# import ssl
# self.ssl_version = ssl.PROTOCOL_SSLv3
def session_start(self, event): def session_start(self, event):
self.send_presence() self.send_presence()
self.get_roster() self.get_roster()
# Most get_*/set_* methods from plugins use Iq stanzas, which # Most get_*/set_* methods from plugins use Iq stanzas, which
# are sent asynchronously. You can almost always provide a # can generate IqError and IqTimeout exceptions
# callback that will be executed when the reply is received. #
# try:
# self.get_roster()
# except IqError as err:
# logging.error('There was an error getting the roster')
# logging.error(err.iq['error']['condition'])
# self.disconnect()
# except IqTimeout:
# logging.error('Server is taking too long to respond')
# self.disconnect()
def message(self, msg): def message(self, msg):
if msg['type'] in ('chat', 'normal'): if msg['type'] in ('chat', 'normal'):
@@ -87,7 +113,7 @@ Here's your first Slixmpp Bot:
if __name__ == '__main__': if __name__ == '__main__':
# Ideally use optparse or argparse to get JID, # Ideally use optparse or argparse to get JID,
# password, and log level. # password, and log level.
logging.basicConfig(level=logging.DEBUG, logging.basicConfig(level=logging.DEBUG,
@@ -95,24 +121,15 @@ Here's your first Slixmpp Bot:
xmpp = EchoBot('somejid@example.com', 'use_getpass') xmpp = EchoBot('somejid@example.com', 'use_getpass')
xmpp.connect() xmpp.connect()
xmpp.process() xmpp.process(block=True)
To read if you come from SleekXMPP
----------------------------------
.. toctree::
:maxdepth: 1
differences
using_asyncio
Getting Started (with Examples) Getting Started (with Examples)
------------------------------- -------------------------------
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
getting_started/echobot getting_started/echobot
getting_started/sendlogout getting_started/sendlogout
getting_started/component getting_started/component
@@ -127,7 +144,8 @@ Tutorials, FAQs, and How To Guides
---------------------------------- ----------------------------------
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
faq
xeps xeps
xmpp_tdg xmpp_tdg
howto/stanzas howto/stanzas
@@ -138,12 +156,12 @@ Tutorials, FAQs, and How To Guides
Plugin Guides Plugin Guides
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
guide_xep_0030 guide_xep_0030
Slixmpp Architecture and Design SleekXMPP Architecture and Design
--------------------------------- ---------------------------------
.. toctree:: .. toctree::
:maxdepth: 3 :maxdepth: 3
@@ -155,7 +173,7 @@ API Reference
------------- -------------
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
event_index event_index
api/clientxmpp api/clientxmpp
api/componentxmpp api/componentxmpp
@@ -166,7 +184,9 @@ API Reference
api/xmlstream/handler api/xmlstream/handler
api/xmlstream/matcher api/xmlstream/matcher
api/xmlstream/xmlstream api/xmlstream/xmlstream
api/xmlstream/scheduler
api/xmlstream/tostring api/xmlstream/tostring
api/xmlstream/filesocket
Core Stanzas Core Stanzas
~~~~~~~~~~~~ ~~~~~~~~~~~~
@@ -177,6 +197,8 @@ Core Stanzas
api/stanza/message api/stanza/message
api/stanza/presence api/stanza/presence
api/stanza/iq api/stanza/iq
api/stanza/error
api/stanza/stream_error
Plugins Plugins
~~~~~~~ ~~~~~~~
@@ -198,14 +220,8 @@ Additional Info
* :ref:`modindex` * :ref:`modindex`
* :ref:`search` * :ref:`search`
SleekXMPP Credits Credits
----------------- -------
.. note::
Those people made SleekXMPP, so you should not bother them if
you have an issue with slixmpp. But its still fair to credit
them for their work.
**Main Author:** `Nathan Fritz <http://andyet.net/team/fritzy>`_ **Main Author:** `Nathan Fritz <http://andyet.net/team/fritzy>`_
`fritzy@netflint.net <xmpp:fritzy@netflint.net?message>`_, `fritzy@netflint.net <xmpp:fritzy@netflint.net?message>`_,

View File

@@ -1,4 +1,4 @@
.. _license: .. _license:
License (MIT) License (MIT)
============= =============

View File

@@ -95,9 +95,9 @@ if "%1" == "qthelp" (
echo. echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^ echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this: .qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Slixmpp.qhcp echo.^> qcollectiongenerator %BUILDDIR%\qthelp\SleekXMPP.qhcp
echo.To view the help file: echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Slixmpp.ghc echo.^> assistant -collectionFile %BUILDDIR%\qthelp\SleekXMPP.ghc
goto end goto end
) )

View File

@@ -1,148 +0,0 @@
.. _using_asyncio:
=============
Using asyncio
=============
Block on IQ sending
~~~~~~~~~~~~~~~~~~~
:meth:`.Iq.send` now returns a :class:`~.Future` so you can easily block with:
.. code-block:: python
result = yield from iq.send()
.. warning::
If the reply is an IQ with an ``error`` type, this will raise an
:class:`.IqError`, and if it timeouts, it will raise an
:class:`.IqTimeout`. Don't forget to catch it.
You can still use callbacks instead.
XEP plugin integration
~~~~~~~~~~~~~~~~~~~~~~
The same changes from the SleekXMPP API apply, so you can do:
.. code-block:: python
iq_info = yield from self.xmpp['xep_0030'].get_info(jid)
But the following will only return a Future:
.. code-block:: python
iq_info = self.xmpp['xep_0030'].get_info(jid)
Callbacks, Event Handlers, and Stream Handlers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
IQ callbacks and :term:`Event Handlers <event handler>` can be coroutine
functions; in this case, they will be scheduled in the event loop using
:meth:`.asyncio.async` and not ran immediately.
A :class:`.CoroutineCallback` class has been added as well for
:term:`Stream Handlers <stream handler>`, which will use
:meth:`.asyncio.async` to schedule the callback.
Running the event loop
~~~~~~~~~~~~~~~~~~~~~~
:meth:`.XMLStream.process` is only a thin wrapper on top of
``loop.run_forever()`` (if ``timeout`` is provided then it will
only run for this amount of time, and if ``forever`` is False it will
run until disconnection).
Therefore you can handle the event loop in any way you like
instead of using ``process()``.
Examples
~~~~~~~~
Blocking until the session is established
-----------------------------------------
This code blocks until the XMPP session is fully established, which
can be useful to make sure external events arent triggering XMPP
callbacks while everything is not ready.
.. code-block:: python
import asyncio, slixmpp
client = slixmpp.ClientXMPP('jid@example', 'password')
client.connected_event = asyncio.Event()
callback = lambda _: client.connected_event.set()
client.add_event_handler('session_start', callback)
client.connect()
loop.run_until_complete(event.wait())
# do some other stuff before running the event loop, e.g.
# loop.run_until_complete(httpserver.init())
client.process()
Use with other asyncio-based libraries
--------------------------------------
This code interfaces with aiohttp to retrieve two pages asynchronously
when the session is established, and then send the HTML content inside
a simple <message>.
.. code-block:: python
import asyncio, aiohttp, slixmpp
@asyncio.coroutine
def get_pythonorg(event):
req = yield from aiohttp.request('get', 'http://www.python.org')
text = yield from req.text
client.send_message(mto='jid2@example', mbody=text)
@asyncio.coroutine
def get_asyncioorg(event):
req = yield from aiohttp.request('get', 'http://www.asyncio.org')
text = yield from req.text
client.send_message(mto='jid3@example', mbody=text)
client = slixmpp.ClientXMPP('jid@example', 'password')
client.add_event_handler('session_start', get_pythonorg)
client.add_event_handler('session_start', get_asyncioorg)
client.connect()
client.process()
Blocking Iq
-----------
This client checks (via XEP-0092) the software used by every entity it
receives a message from. After this, it sends a message to a specific
JID indicating its findings.
.. code-block:: python
import asyncio, slixmpp
class ExampleClient(slixmpp.ClientXMPP):
def __init__(self, *args, **kwargs):
slixmpp.ClientXMPP.__init__(self, *args, **kwargs)
self.register_plugin('xep_0092')
self.add_event_handler('message', self.on_message)
@asyncio.coroutine
def on_message(self, event):
# You should probably handle IqError and IqTimeout exceptions here
# but this is an example.
version = yield from self['xep_0092'].get_version(message['from'])
text = "%s sent me a message, he runs %s" % (message['from'],
version['software_version']['name'])
self.send_message(mto='master@example.tld', mbody=text)
client = ExampleClient('jid@example', 'password')
client.connect()
client.process()

View File

@@ -4,9 +4,9 @@ Supported XEPS
======= ============================= ================ ======= ============================= ================
XEP Description Notes XEP Description Notes
======= ============================= ================ ======= ============================= ================
`0004`_ Data forms `0004`_ Data forms
`0009`_ Jabber RPC `0009`_ Jabber RPC
`0012`_ Last Activity `0012`_ Last Activity
`0030`_ Service Discovery `0030`_ Service Discovery
`0033`_ Extended Stanza Addressing `0033`_ Extended Stanza Addressing
`0045`_ Multi-User Chat (MUC) Client-side only `0045`_ Multi-User Chat (MUC) Client-side only

View File

@@ -1,20 +1,20 @@
Following *XMPP: The Definitive Guide* Following *XMPP: The Definitive Guide*
====================================== ======================================
Slixmpp was featured in the first edition of the O'Reilly book SleekXMPP was featured in the first edition of the O'Reilly book
`XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271/>`_ `XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271/>`_
by Peter Saint-Andre, Kevin Smith, and Remko Tronçon. The original source code by Peter Saint-Andre, Kevin Smith, and Remko Tronçon. The original source code
for the book's examples can be found at http://github.com/remko/xmpp-tdg. An for the book's examples can be found at http://github.com/remko/xmpp-tdg. An
updated version of the source code, maintained to stay current with the latest updated version of the source code, maintained to stay current with the latest
Slixmpp release, is available at http://github.com/legastero/xmpp-tdg. SleekXMPP release, is available at http://github.com/legastero/xmpp-tdg.
However, since publication, Slixmpp has advanced from version 0.2.1 to version However, since publication, SleekXMPP has advanced from version 0.2.1 to version
1.0 and there have been several major API changes. The most notable is the 1.0 and there have been several major API changes. The most notable is the
introduction of :term:`stanza objects <stanza object>` which have simplified and introduction of :term:`stanza objects <stanza object>` which have simplified and
standardized interactions with the XMPP XML stream. standardized interactions with the XMPP XML stream.
What follows is a walk-through of *The Definitive Guide* highlighting the What follows is a walk-through of *The Definitive Guide* highlighting the
changes needed to make the code examples work with version 1.0 of Slixmpp. changes needed to make the code examples work with version 1.0 of SleekXMPP.
These changes have been kept to a minimum to preserve the correlation with These changes have been kept to a minimum to preserve the correlation with
the book's explanations, so be aware that some code may not use current best the book's explanations, so be aware that some code may not use current best
practices. practices.
@@ -36,7 +36,7 @@ Updated Code
.. code-block:: python .. code-block:: python
def handleIncomingMessage(self, message): def handleIncomingMessage(self, message):
self.xmpp.send_message(message["from"], message["body"]) self.xmpp.sendMessage(message["from"], message["body"])
`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_ | `View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_ |
`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_ `View original code <http://github.com/remko/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_
@@ -47,7 +47,7 @@ Example 14-1. (Page 215)
**CheshiR IM bot implementation.** **CheshiR IM bot implementation.**
The main event handling method in the Bot class is meant to process both message The main event handling method in the Bot class is meant to process both message
events and presence update events. With the new changes in Slixmpp 1.0, events and presence update events. With the new changes in SleekXMPP 1.0,
extracting a CheshiR status "message" from both types of stanzas extracting a CheshiR status "message" from both types of stanzas
requires accessing different attributes. In the case of a message stanza, the requires accessing different attributes. In the case of a message stanza, the
``"body"`` attribute would contain the CheshiR message. For a presence event, ``"body"`` attribute would contain the CheshiR message. For a presence event,
@@ -72,21 +72,21 @@ Updated Code
.. code-block:: python .. code-block:: python
def handleIncomingXMPPEvent(self, event): def handleIncomingXMPPEvent(self, event):
msgLocations = {slixmpp.stanza.presence.Presence: "status", msgLocations = {sleekxmpp.stanza.presence.Presence: "status",
slixmpp.stanza.message.Message: "body"} sleekxmpp.stanza.message.Message: "body"}
message = event[msgLocations[type(event)]] message = event[msgLocations[type(event)]]
user = self.backend.getUserFromJID(event["from"].jid) user = self.backend.getUserFromJID(event["from"].jid)
if user is not None: if user is not None:
self.backend.addMessageFromUser(message, user) self.backend.addMessageFromUser(message, user)
def handleMessageAddedToBackend(self, message) : def handleMessageAddedToBackend(self, message) :
body = message.user + ": " + message.text body = message.user + ": " + message.text
htmlBody = "<p><a href='%(uri)s'>%(user)s</a>: %(message)s</p>" % { htmlBody = "<p><a href='%(uri)s'>%(user)s</a>: %(message)s</p>" % {
"uri": self.url + "/" + message.user, "uri": self.url + "/" + message.user,
"user" : message.user, "message" : message.text } "user" : message.user, "message" : message.text }
for subscriberJID in self.backend.getSubscriberJIDs(message.user) : for subscriberJID in self.backend.getSubscriberJIDs(message.user) :
self.xmpp.send_message(subscriberJID, body, mhtml=htmlBody) self.xmpp.sendMessage(subscriberJID, body, mhtml=htmlBody)
`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_ | `View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_ |
`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_ `View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_
@@ -102,7 +102,7 @@ Example 14-3. (Page 217)
The main difference for the configurable IM bot is the handling for the The main difference for the configurable IM bot is the handling for the
data form in ``handleConfigurationCommand``. The test for equality data form in ``handleConfigurationCommand``. The test for equality
with the string ``"1"`` is no longer required; Slixmpp converts with the string ``"1"`` is no longer required; SleekXMPP converts
boolean data form fields to the values ``True`` and ``False`` boolean data form fields to the values ``True`` and ``False``
automatically. automatically.
@@ -145,7 +145,7 @@ Example 14-4. (Page 220)
Like several previous examples, a needed change is to replace Like several previous examples, a needed change is to replace
``subscription["from"]`` with ``subscription["from"].jid`` because the ``subscription["from"]`` with ``subscription["from"].jid`` because the
``BaseXMPP`` method ``make_presence`` requires the JID to be a string. ``BaseXMPP`` method ``makePresence`` requires the JID to be a string.
A correction needs to be made in ``handleXMPPPresenceProbe`` because a line was A correction needs to be made in ``handleXMPPPresenceProbe`` because a line was
left out of the original implementation; the variable ``user`` is undefined. The left out of the original implementation; the variable ``user`` is undefined. The
@@ -154,7 +154,7 @@ JID of the user can be extracted from the presence stanza's ``from`` attribute.
Since this implementation of CheshiR uses an XMPP component, it must Since this implementation of CheshiR uses an XMPP component, it must
include a ``from`` attribute in all messages that it sends. Adding the include a ``from`` attribute in all messages that it sends. Adding the
``from`` attribute is done by including ``mfrom=self.xmpp.jid`` in calls to ``from`` attribute is done by including ``mfrom=self.xmpp.jid`` in calls to
``self.xmpp.send_message``. ``self.xmpp.sendMessage``.
Updated Code Updated Code
~~~~~~~~~~~~ ~~~~~~~~~~~~
@@ -162,19 +162,19 @@ Updated Code
.. code-block:: python .. code-block:: python
def handleXMPPPresenceProbe(self, event) : def handleXMPPPresenceProbe(self, event) :
self.xmpp.send_presence(pto = event["from"]) self.xmpp.sendPresence(pto = event["from"])
def handleXMPPPresenceSubscription(self, subscription) : def handleXMPPPresenceSubscription(self, subscription) :
if subscription["type"] == "subscribe" : if subscription["type"] == "subscribe" :
userJID = subscription["from"].jid userJID = subscription["from"].jid
self.xmpp.send_presence_subscription(pto=userJID, ptype="subscribed") self.xmpp.sendPresenceSubscription(pto=userJID, ptype="subscribed")
self.xmpp.send_presence(pto = userJID) self.xmpp.sendPresence(pto = userJID)
self.xmpp.send_presence_subscription(pto=userJID, ptype="subscribe") self.xmpp.sendPresenceSubscription(pto=userJID, ptype="subscribe")
def handleMessageAddedToBackend(self, message) : def handleMessageAddedToBackend(self, message) :
body = message.user + ": " + message.text body = message.user + ": " + message.text
for subscriberJID in self.backend.getSubscriberJIDs(message.user) : for subscriberJID in self.backend.getSubscriberJIDs(message.user) :
self.xmpp.send_message(subscriberJID, body, mfrom=self.xmpp.jid) self.xmpp.sendMessage(subscriberJID, body, mfrom=self.xmpp.jid)
`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_ | `View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_ |
`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_ `View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_
@@ -192,7 +192,7 @@ After applying the changes from Example 14-4 above, the registrable component
implementation should work correctly. implementation should work correctly.
.. tip:: .. tip::
To see how to implement in-band registration as a Slixmpp plugin, To see how to implement in-band registration as a SleekXMPP plugin,
see the tutorial :ref:`tutorial-create-plugin`. see the tutorial :ref:`tutorial-create-plugin`.
`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/RegistrableComponent.py>`_ | `View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/RegistrableComponent.py>`_ |
@@ -203,13 +203,13 @@ Example 14-7. (Page 225)
**Extended CheshiR IM server component implementation.** **Extended CheshiR IM server component implementation.**
.. note:: .. note::
Since the CheshiR examples build on each other, see previous Since the CheshiR examples build on each other, see previous
sections for corrections to code that is not marked as new in the book sections for corrections to code that is not marked as new in the book
example. example.
While the final code example can look daunting with all of the changes While the final code example can look daunting with all of the changes
made, it requires very few modifications to work with the latest version of made, it requires very few modifications to work with the latest version of
Slixmpp. Most differences are the result of CheshiR's backend functions SleekXMPP. Most differences are the result of CheshiR's backend functions
expecting JIDs to be strings so that they can be stripped to bare JIDs. To expecting JIDs to be strings so that they can be stripped to bare JIDs. To
resolve these, use the ``jid`` attribute of the JID objects. Also, resolve these, use the ``jid`` attribute of the JID objects. Also,
references to ``"message"`` and ``"jid"`` attributes need to references to ``"message"`` and ``"jid"`` attributes need to
@@ -239,11 +239,11 @@ Updated Code
userJID = subscription["from"].jid userJID = subscription["from"].jid
user = self.backend.getUserFromJID(userJID) user = self.backend.getUserFromJID(userJID)
contactJID = subscription["to"] contactJID = subscription["to"]
self.xmpp.send_presence_subscription( self.xmpp.sendPresenceSubscription(
pfrom=contactJID, pto=userJID, ptype="subscribed", pnick=user) pfrom=contactJID, pto=userJID, ptype="subscribed", pnick=user)
self.sendPresenceOfContactToUser(contactJID=contactJID, userJID=userJID) self.sendPresenceOfContactToUser(contactJID=contactJID, userJID=userJID)
if contactJID == self.componentDomain : if contactJID == self.componentDomain :
self.sendAllContactSubscriptionRequestsToUser(userJID) self.sendAllContactSubscriptionRequestsToUser(userJID)
`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_ | `View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_ |
`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_ `View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_

View File

@@ -1,182 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Implementation of xeps for Internet of Things
http://wiki.xmpp.org/web/Tech_pages/IoT_systems
Copyright (C) 2013 Sustainable Innovation, Joachim.lindborg@sust.se
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from os.path import basename, join as pjoin
from argparse import ArgumentParser
from urllib import urlopen
from getpass import getpass
import slixmpp
from slixmpp.plugins.xep_0323.device import Device
#from slixmpp.exceptions import IqError, IqTimeout
class IoT_TestDevice(slixmpp.ClientXMPP):
"""
A simple IoT device that can act as server or client
"""
def __init__(self, jid, password):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.add_event_handler("session_start", self.session_start)
self.add_event_handler("message", self.message)
self.device=None
self.releaseMe=False
self.beServer=True
self.clientJID=None
def datacallback(self,from_jid,result,nodeId=None,timestamp=None,fields=None,error_msg=None):
"""
This method will be called when you ask another IoT device for data with the xep_0323
se script below for the registration of the callback
"""
logging.debug("we got data %s from %s",str(result),from_jid)
def beClientOrServer(self,server=True,clientJID=None ):
if server:
self.beServer=True
self.clientJID=None
else:
self.beServer=False
self.clientJID=clientJID
def testForRelease(self):
# todo thread safe
return self.releaseMe
def doReleaseMe(self):
# todo thread safe
self.releaseMe=True
def addDevice(self, device):
self.device=device
def session_start(self, event):
self.send_presence()
self.get_roster()
# tell your preffered friend that you are alive
self.send_message(mto='jocke@jabber.sust.se', mbody=self.boundjid.bare +' is now online use xep_323 stanza to talk to me')
if not(self.beServer):
session=self['xep_0323'].request_data(self.boundjid.full,self.clientJID,self.datacallback)
def message(self, msg):
if msg['type'] in ('chat', 'normal'):
logging.debug("got normal chat message" + str(msg))
ip=urlopen('http://icanhazip.com').read()
msg.reply("Hi I am " + self.boundjid.full + " and I am on IP " + ip).send()
else:
logging.debug("got unknown message type %s", str(msg['type']))
class TheDevice(Device):
"""
This is the actual device object that you will use to get information from your real hardware
You will be called in the refresh method when someone is requesting information from you
"""
def __init__(self,nodeId):
Device.__init__(self,nodeId)
self.counter=0
def refresh(self,fields):
"""
the implementation of the refresh method
"""
self._set_momentary_timestamp(self._get_timestamp())
self.counter+=self.counter
self._add_field_momentary_data(self, "Temperature", self.counter)
if __name__ == '__main__':
# Setup the command line arguments.
#
# This script can act both as
# "server" an IoT device that can provide sensorinformation
# python IoT_TestDevice.py -j "serverjid@yourdomain.com" -p "password" -n "TestIoT" --debug
#
# "client" an IoT device or other party that would like to get data from another device
parser = ArgumentParser()
# Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR",
action="store_const", dest="loglevel",
const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG",
action="store_const", dest="loglevel",
const=logging.DEBUG, default=logging.INFO)
parser.add_argument("-t", "--pingto", help="set jid to ping",
action="store", type="string", dest="pingjid",
default=None)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
# IoT test
parser.add_argument("-c", "--sensorjid", dest="sensorjid",
help="Another device to call for data on", default=None)
parser.add_argument("-n", "--nodeid", dest="nodeid",
help="I am a device get ready to be called", default=None)
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
xmpp = IoT_TestDevice(args.jid,args.password)
xmpp.register_plugin('xep_0030')
#xmpp['xep_0030'].add_feature(feature='urn:xmpp:iot:sensordata',
# node=None,
# jid=None)
xmpp.register_plugin('xep_0323')
xmpp.register_plugin('xep_0325')
if args.nodeid:
# xmpp['xep_0030'].add_feature(feature='urn:xmpp:sn',
# node=args.nodeid,
# jid=xmpp.boundjid.full)
myDevice = TheDevice(args.nodeid);
# myDevice._add_field(name="Relay", typename="numeric", unit="Bool");
myDevice._add_field(name="Temperature", typename="numeric", unit="C")
myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
xmpp['xep_0323'].register_node(nodeId=args.nodeid, device=myDevice, commTimeout=10);
xmpp.beClientOrServer(server=True)
while not(xmpp.testForRelease()):
xmpp.connect()
xmpp.process(block=True)
logging.debug("lost connection")
if args.sensorjid:
logging.debug("will try to call another device for data")
xmpp.beClientOrServer(server=False,clientJID=args.sensorjid)
xmpp.connect()
xmpp.process(block=True)
logging.debug("ready ending")
else:
print("noopp didn't happen")

View File

@@ -1,30 +1,41 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class CommandBot(slixmpp.ClientXMPP): class CommandBot(sleekxmpp.ClientXMPP):
""" """
A simple Slixmpp bot that provides a basic A simple SleekXMPP bot that provides a basic
adhoc command. adhoc command.
""" """
def __init__(self, jid, password): def __init__(self, jid, password):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The session_start event will be triggered when # The session_start event will be triggered when
# the bot establishes its connection with the server # the bot establishes its connection with the server
@@ -33,7 +44,7 @@ class CommandBot(slixmpp.ClientXMPP):
# our roster. # our roster.
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -47,7 +58,7 @@ class CommandBot(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
# We add the command after session_start has fired # We add the command after session_start has fired
# to ensure that the correct full JID is used. # to ensure that the correct full JID is used.
@@ -68,7 +79,7 @@ class CommandBot(slixmpp.ClientXMPP):
session. Additional, custom data may be saved session. Additional, custom data may be saved
here to persist across handler callbacks. here to persist across handler callbacks.
""" """
form = self['xep_0004'].make_form('form', 'Greeting') form = self['xep_0004'].makeForm('form', 'Greeting')
form['instructions'] = 'Send a custom greeting to a JID' form['instructions'] = 'Send a custom greeting to a JID'
form.addField(var='greeting', form.addField(var='greeting',
ftype='text-single', ftype='text-single',
@@ -132,42 +143,62 @@ class CommandBot(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
# Setup the CommandBot and register plugins. Note that while plugins may # Setup the CommandBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = CommandBot(args.jid, args.password) xmpp = CommandBot(opts.jid, opts.password)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0050') # Adhoc Commands xmpp.register_plugin('xep_0050') # Adhoc Commands
xmpp.register_plugin('xep_0199', {'keepalive': True, 'frequency':15}) 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() # 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.")

View File

@@ -1,30 +1,41 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class CommandUserBot(slixmpp.ClientXMPP): class CommandUserBot(sleekxmpp.ClientXMPP):
""" """
A simple Slixmpp bot that uses the adhoc command A simple SleekXMPP bot that uses the adhoc command
provided by the adhoc_provider.py example. provided by the adhoc_provider.py example.
""" """
def __init__(self, jid, password, other, greeting): def __init__(self, jid, password, other, greeting):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.command_provider = other self.command_provider = other
self.greeting = greeting self.greeting = greeting
@@ -37,7 +48,7 @@ class CommandUserBot(slixmpp.ClientXMPP):
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start)
self.add_event_handler("message", self.message) self.add_event_handler("message", self.message)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -51,7 +62,7 @@ class CommandUserBot(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
# We first create a session dictionary containing: # We first create a session dictionary containing:
# 'next' -- the handler to execute on a successful response # 'next' -- the handler to execute on a successful response
@@ -94,7 +105,7 @@ class CommandUserBot(slixmpp.ClientXMPP):
# label="Your greeting" /> # label="Your greeting" />
# </x> # </x>
form = self['xep_0004'].make_form(ftype='submit') form = self['xep_0004'].makeForm(ftype='submit')
form.addField(var='greeting', form.addField(var='greeting',
value=session['greeting']) value=session['greeting'])
@@ -131,49 +142,69 @@ class CommandUserBot(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
parser.add_argument("-o", "--other", dest="other", optp.add_option("-o", "--other", dest="other",
help="JID providing commands") help="JID providing commands")
parser.add_argument("-g", "--greeting", dest="greeting", optp.add_option("-g", "--greeting", dest="greeting",
help="Greeting") help="Greeting")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
if args.other is None: if opts.other is None:
args.other = input("JID Providing Commands: ") opts.other = raw_input("JID Providing Commands: ")
if args.greeting is None: if opts.greeting is None:
args.greeting = input("Greeting: ") opts.greeting = raw_input("Greeting: ")
# Setup the CommandBot and register plugins. Note that while plugins may # Setup the CommandBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = CommandUserBot(args.jid, args.password, args.other, args.greeting) xmpp = CommandUserBot(opts.jid, opts.password, opts.other, opts.greeting)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0050') # Adhoc Commands 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process(forever=False) # 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.")

View File

@@ -1,36 +1,47 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class AdminCommands(slixmpp.ClientXMPP): class AdminCommands(sleekxmpp.ClientXMPP):
""" """
A simple Slixmpp bot that uses admin commands to A simple SleekXMPP bot that uses admin commands to
add a new user to a server. add a new user to a server.
""" """
def __init__(self, jid, password, command): def __init__(self, jid, password, command):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.command = command self.command = command
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -44,7 +55,7 @@ class AdminCommands(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
def command_success(iq, session): def command_success(iq, session):
print('Command completed') print('Command completed')
@@ -70,13 +81,13 @@ class AdminCommands(slixmpp.ClientXMPP):
for var, field in form['fields'].items(): for var, field in form['fields'].items():
if var != 'FORM_TYPE': if var != 'FORM_TYPE':
if field['type'] == 'boolean': if field['type'] == 'boolean':
answers[var] = input('%s (y/n): ' % field['label']) answers[var] = raw_input('%s (y/n): ' % field['label'])
if answers[var].lower() in ('1', 'true', 'y', 'yes'): if answers[var].lower() in ('1', 'true', 'y', 'yes'):
answers[var] = '1' answers[var] = '1'
else: else:
answers[var] = '0' answers[var] = '0'
else: else:
answers[var] = input('%s: ' % field['label']) answers[var] = raw_input('%s: ' % field['label'])
else: else:
answers['FORM_TYPE'] = field['value'] answers['FORM_TYPE'] = field['value']
form['type'] = 'submit' form['type'] = 'submit'
@@ -105,43 +116,63 @@ class AdminCommands(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
parser.add_argument("-c", "--command", dest="command", optp.add_option("-c", "--command", dest="command",
help="admin command to use") help="admin command to use")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
if args.command is None: if opts.command is None:
args.command = input("Admin command: ") opts.command = raw_input("Admin command: ")
# Setup the CommandBot and register plugins. Note that while plugins may # Setup the CommandBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = AdminCommands(args.jid, args.password, args.command) xmpp = AdminCommands(opts.jid, opts.password, opts.command)
xmpp.register_plugin('xep_0133') # Service Administration xmpp.register_plugin('xep_0133') # Service Administration
# 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() # 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.")

View File

@@ -1,99 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2015 Emmanuel Gil Peyrot
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from getpass import getpass
from argparse import ArgumentParser
import slixmpp
from slixmpp.exceptions import XMPPError
log = logging.getLogger(__name__)
class AnswerConfirm(slixmpp.ClientXMPP):
"""
A basic client demonstrating how to confirm or deny an HTTP request.
"""
def __init__(self, jid, password, trusted):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.add_event_handler("http_confirm", self.confirm)
self.add_event_handler("session_start", self.start)
def start(self, *args):
self.make_presence().send()
def prompt(self, stanza):
confirm = stanza['confirm']
print('Received confirm request %s from %s to access %s using '
'method %s' % (
confirm['id'], stanza['from'], confirm['url'],
confirm['method'])
)
result = input("Do you accept (y/N)? ")
return 'y' == result.lower()
def confirm(self, stanza):
if self.prompt(stanza):
reply = stanza.reply()
else:
reply = stanza.reply()
reply.enable('error')
reply['error']['type'] = 'auth'
reply['error']['code'] = '401'
reply['error']['condition'] = 'not-authorized'
reply.append(stanza['confirm'])
reply.send()
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser()
parser.add_argument("-q","--quiet", help="set logging to ERROR",
action="store_const",
dest="loglevel",
const=logging.ERROR,
default=logging.INFO)
parser.add_argument("-d","--debug", help="set logging to DEBUG",
action="store_const",
dest="loglevel",
const=logging.DEBUG,
default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
# Other options.
parser.add_argument("-t", "--trusted", nargs='*',
help="List of trusted JIDs")
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
xmpp = AnswerConfirm(args.jid, args.password, args.trusted)
xmpp.register_plugin('xep_0070')
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process()

View File

@@ -1,124 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2015 Emmanuel Gil Peyrot
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import sys
import logging
from getpass import getpass
from argparse import ArgumentParser
import slixmpp
from slixmpp.exceptions import XMPPError, IqError
from slixmpp import asyncio
log = logging.getLogger(__name__)
class AskConfirm(slixmpp.ClientXMPP):
"""
A basic client asking an entity if they confirm the access to an HTTP URL.
"""
def __init__(self, jid, password, recipient, id, url, method):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.recipient = recipient
self.id = id
self.url = url
self.method = method
# Will be used to set the proper exit code.
self.confirmed = asyncio.Future()
self.add_event_handler("session_start", self.start)
self.add_event_handler("message", self.start)
self.add_event_handler("http_confirm_message", self.confirm)
def confirm(self, message):
print(message)
if message['confirm']['id'] == self.id:
if message['type'] == 'error':
self.confirmed.set_result(False)
else:
self.confirmed.set_result(True)
async def start(self, event):
log.info('Sending confirm request %s to %s who wants to access %s using '
'method %s...' % (self.id, self.recipient, self.url, self.method))
try:
confirmed = await self['xep_0070'].ask_confirm(self.recipient,
id=self.id,
url=self.url,
method=self.method,
message='Plz say yes or no for {method} {url} ({id}).')
if isinstance(confirmed, slixmpp.Message):
confirmed = await self.confirmed
else:
confirmed = True
except IqError:
confirmed = False
if confirmed:
print('Confirmed')
else:
print('Denied')
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser()
parser.add_argument("-q","--quiet", help="set logging to ERROR",
action="store_const",
dest="loglevel",
const=logging.ERROR,
default=logging.INFO)
parser.add_argument("-d","--debug", help="set logging to DEBUG",
action="store_const",
dest="loglevel",
const=logging.DEBUG,
default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
# Other options.
parser.add_argument("-r", "--recipient", required=True,
help="Recipient JID")
parser.add_argument("-i", "--id", required=True,
help="id TODO")
parser.add_argument("-u", "--url", required=True,
help="URL the user tried to access")
parser.add_argument("-m", "--method", required=True,
help="HTTP method used")
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
xmpp = AskConfirm(args.jid, args.password, args.recipient, args.id,
args.url, args.method)
xmpp.register_plugin('xep_0070')
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process(forever=False)
sys.exit(0 if xmpp.confirmed else 1)

View File

@@ -1,37 +1,48 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp import sleekxmpp
from slixmpp import ClientXMPP, Iq from sleekxmpp import ClientXMPP, Iq
from slixmpp.exceptions import IqError, IqTimeout, XMPPError from sleekxmpp.exceptions import IqError, IqTimeout, XMPPError
from slixmpp.xmlstream import register_stanza_plugin from sleekxmpp.xmlstream import register_stanza_plugin
from slixmpp.xmlstream.handler import Callback from sleekxmpp.xmlstream.handler import Callback
from slixmpp.xmlstream.matcher import StanzaPath from sleekxmpp.xmlstream.matcher import StanzaPath
from stanza import Action from stanza import Action
# 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class ActionBot(slixmpp.ClientXMPP):
class ActionBot(sleekxmpp.ClientXMPP):
""" """
A simple Slixmpp bot that receives a custom stanza A simple SleekXMPP bot that receives a custom stanza
from another client. from another client.
""" """
def __init__(self, jid, password): def __init__(self, jid, password):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The session_start event will be triggered when # The session_start event will be triggered when
# the bot establishes its connection with the server # the bot establishes its connection with the server
@@ -40,17 +51,18 @@ class ActionBot(slixmpp.ClientXMPP):
# our roster. # our roster.
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start)
self.register_handler( self.registerHandler(
Callback('Some custom iq', Callback('Some custom iq',
StanzaPath('iq@type=set/action'), StanzaPath('iq@type=set/action'),
self._handle_action)) self._handle_action))
self.add_event_handler('custom_action', self.add_event_handler('custom_action',
self._handle_action_event) self._handle_action_event,
threaded=True)
register_stanza_plugin(Iq, Action) register_stanza_plugin(Iq, Action)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -73,68 +85,89 @@ class ActionBot(slixmpp.ClientXMPP):
""" """
self.event('custom_action', iq) self.event('custom_action', iq)
async def _handle_action_event(self, iq): def _handle_action_event(self, iq):
""" """
Respond to the custom action event. Respond to the custom action event.
Since one of the actions is to disconnect, this
event handler needs to be run in threaded mode, by
using `threaded=True` in the `add_event_handler` call.
""" """
method = iq['action']['method'] method = iq['action']['method']
param = iq['action']['param'] param = iq['action']['param']
if method == 'is_prime' and param == '2': if method == 'is_prime' and param == '2':
print("got message: %s" % iq) print("got message: %s" % iq)
rep = iq.reply() iq.reply()
rep['action']['status'] = 'done' iq['action']['status'] = 'done'
await rep.send() iq.send()
elif method == 'bye': elif method == 'bye':
print("got message: %s" % iq) print("got message: %s" % iq)
rep = iq.reply()
rep['action']['status'] = 'done'
await rep.send()
self.disconnect() self.disconnect()
else: else:
print("got message: %s" % iq) print("got message: %s" % iq)
rep = iq.reply() iq.reply()
rep['action']['status'] = 'error' iq['action']['status'] = 'error'
await rep.send() iq.send()
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
# Setup the CommandBot and register plugins. Note that while plugins may # Setup the CommandBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = ActionBot(args.jid, args.password) xmpp = ActionBot(opts.jid, opts.password)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0050') # Adhoc Commands xmpp.register_plugin('xep_0050') # Adhoc Commands
xmpp.register_plugin('xep_0199', {'keepalive': True, 'frequency':15}) 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() # 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.")

View File

@@ -1,35 +1,46 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp import sleekxmpp
from slixmpp import Iq from sleekxmpp import Iq
from slixmpp.exceptions import XMPPError from sleekxmpp.exceptions import XMPPError
from slixmpp.xmlstream import register_stanza_plugin from sleekxmpp.xmlstream import register_stanza_plugin
from stanza import Action from stanza import Action
# 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class ActionUserBot(slixmpp.ClientXMPP):
class ActionUserBot(sleekxmpp.ClientXMPP):
""" """
A simple Slixmpp bot that sends a custom action stanza A simple SleekXMPP bot that sends a custom action stanza
to another client. to another client.
""" """
def __init__(self, jid, password, other): def __init__(self, jid, password, other):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.action_provider = other self.action_provider = other
@@ -38,12 +49,12 @@ class ActionUserBot(slixmpp.ClientXMPP):
# and the XML streams are ready for use. We want to # and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize # listen for this event so that we we can initialize
# our roster. # our roster.
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start, threaded=True)
self.add_event_handler("message", self.message) self.add_event_handler("message", self.message)
register_stanza_plugin(Iq, Action) register_stanza_plugin(Iq, Action)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -57,11 +68,11 @@ class ActionUserBot(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
await self.send_custom_iq() self.send_custom_iq()
async def send_custom_iq(self): def send_custom_iq(self):
"""Create and send two custom actions. """Create and send two custom actions.
If the first action was successful, then send If the first action was successful, then send
@@ -74,16 +85,18 @@ class ActionUserBot(slixmpp.ClientXMPP):
iq['action']['param'] = '2' iq['action']['param'] = '2'
try: try:
resp = await iq.send() resp = iq.send()
if resp['action']['status'] == 'done': if resp['action']['status'] == 'done':
#sending bye #sending bye
iq2 = self.Iq() iq2 = self.Iq()
iq2['to'] = self.action_provider iq2['to'] = self.action_provider
iq2['type'] = 'set' iq2['type'] = 'set'
iq2['action']['method'] = 'bye' iq2['action']['method'] = 'bye'
await iq2.send() iq2.send(block=False)
self.disconnect() # The wait=True delays the disconnect until the queue
# of stanzas to be sent becomes empty.
self.disconnect(wait=True)
except XMPPError: except XMPPError:
print('There was an error sending the custom action.') print('There was an error sending the custom action.')
@@ -98,45 +111,65 @@ class ActionUserBot(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
parser.add_argument("-o", "--other", dest="other", optp.add_option("-o", "--other", dest="other",
help="JID providing custom stanza") help="JID providing custom stanza")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
if args.other is None: if opts.other is None:
args.other = input("JID Providing custom stanza: ") opts.other = raw_input("JID Providing custom stanza: ")
# Setup the CommandBot and register plugins. Note that while plugins may # Setup the CommandBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = ActionUserBot(args.jid, args.password, args.other) xmpp = ActionUserBot(opts.jid, opts.password, opts.other)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0050') # Adhoc Commands 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() # 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.")

View File

@@ -1,30 +1,30 @@
from slixmpp.xmlstream import ElementBase from sleekxmpp.xmlstream import ElementBase
class Action(ElementBase): class Action(ElementBase):
""" """
A stanza class for XML content of the form: A stanza class for XML content of the form:
<action xmlns="slixmpp:custom:actions"> <action xmlns="sleekxmpp:custom:actions">
<method>X</method> <method>X</method>
<param>X</param> <param>X</param>
<status>X</status> <status>X</status>
</action> </action>
""" """
#: The `name` field refers to the basic XML tag name of the #: The `name` field refers to the basic XML tag name of the
#: stanza. Here, the tag name will be 'action'. #: stanza. Here, the tag name will be 'action'.
name = 'action' name = 'action'
#: The namespace of the main XML tag. #: The namespace of the main XML tag.
namespace = 'slixmpp:custom:actions' namespace = 'sleekxmpp:custom:actions'
#: The `plugin_attrib` value is the name that can be used #: The `plugin_attrib` value is the name that can be used
#: with a parent stanza to access this stanza. For example #: with a parent stanza to access this stanza. For example
#: from an Iq stanza object, accessing: #: from an Iq stanza object, accessing:
#: #:
#: iq['action'] #: iq['action']
#: #:
#: would reference an Action object, and will even create #: would reference an Action object, and will even create
#: an Action object and append it to the Iq stanza if #: an Action object and append it to the Iq stanza if
#: one doesn't already exist. #: one doesn't already exist.
@@ -41,7 +41,7 @@ class Action(ElementBase):
#: del action['status'] #: del action['status']
#: #:
#: to set, get, or remove its values. #: to set, get, or remove its values.
interfaces = {'method', 'param', 'status'} interfaces = set(('method', 'param', 'status'))
#: By default, values in the `interfaces` set are mapped to #: By default, values in the `interfaces` set are mapped to
#: attribute values. This can be changed such that an interface #: attribute values. This can be changed such that an interface
@@ -49,8 +49,8 @@ class Action(ElementBase):
#: the sub_interfaces set. For example, here all interfaces #: the sub_interfaces set. For example, here all interfaces
#: are marked as sub_interfaces, and so the XML produced will #: are marked as sub_interfaces, and so the XML produced will
#: look like: #: look like:
#: #:
#: <action xmlns="slixmpp:custom:actions"> #: <action xmlns="sleekxmpp:custom:actions">
#: <method>foo</method> #: <method>foo</method>
#: </action> #: </action>
sub_interfaces = interfaces sub_interfaces = interfaces

View File

@@ -1,23 +1,35 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp import sleekxmpp
from slixmpp.exceptions import IqError, IqTimeout from sleekxmpp.exceptions import IqError, IqTimeout
class Disco(slixmpp.ClientXMPP): # 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class Disco(sleekxmpp.ClientXMPP):
""" """
A demonstration for using basic service discovery. A demonstration for using basic service discovery.
@@ -30,7 +42,7 @@ class Disco(slixmpp.ClientXMPP):
""" """
def __init__(self, jid, password, target_jid, target_node='', get=''): def __init__(self, jid, password, target_jid, target_node='', get=''):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
# Using service discovery requires the XEP-0030 plugin. # Using service discovery requires the XEP-0030 plugin.
self.register_plugin('xep_0030') self.register_plugin('xep_0030')
@@ -51,9 +63,9 @@ class Disco(slixmpp.ClientXMPP):
# and the XML streams are ready for use. We want to # and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize # listen for this event so that we we can initialize
# our roster. # our roster.
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start, threaded=True)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -69,21 +81,27 @@ class Disco(slixmpp.ClientXMPP):
event does not provide any additional event does not provide any additional
data. data.
""" """
await self.get_roster() self.get_roster()
self.send_presence() self.send_presence()
try: try:
if self.get in self.info_types: 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. # function using the callback parameter.
info = await self['xep_0030'].get_info(jid=self.target_jid, info = self['xep_0030'].get_info(jid=self.target_jid,
node=self.target_node) node=self.target_node,
if self.get in self.items_types: block=True)
elif self.get in self.items_types:
# The same applies from above. Listen for the # The same applies from above. Listen for the
# disco_items event or pass a callback function # disco_items event or pass a callback function
# if you need to process a non-blocking request. # if you need to process a non-blocking request.
items = await self['xep_0030'].get_items(jid=self.target_jid, items = self['xep_0030'].get_items(jid=self.target_jid,
node=self.target_node) node=self.target_node,
if self.get not in self.info_types and self.get not in self.items_types: block=True)
else:
logging.error("Invalid disco request type.") logging.error("Invalid disco request type.")
return return
except IqError as e: except IqError as e:
@@ -118,42 +136,69 @@ class Disco(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser(description=Disco.__doc__) optp = OptionParser()
optp.version = '%%prog 0.1'
optp.usage = "Usage: %%prog [options] %s <jid> [<node>]" % \
'all|info|items|identities|features'
parser.add_argument("-q","--quiet", help="set logging to ERROR", optp.add_option('-q','--quiet', help='set logging to ERROR',
action="store_const", action='store_const',
dest="loglevel", dest='loglevel',
const=logging.ERROR, const=logging.ERROR,
default=logging.ERROR) default=logging.ERROR)
parser.add_argument("-d","--debug", help="set logging to DEBUG", optp.add_option('-d','--debug', help='set logging to DEBUG',
action="store_const", action='store_const',
dest="loglevel", dest='loglevel',
const=logging.DEBUG, const=logging.DEBUG,
default=logging.ERROR) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
parser.add_argument("query", choices=["all", "info", "items", "identities", "features"]) opts,args = optp.parse_args()
parser.add_argument("target_jid")
parser.add_argument("node", nargs='?')
args = parser.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if len(args) < 2:
args.jid = input("Username: ") optp.print_help()
if args.password is None: exit()
args.password = getpass("Password: ")
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. # Setup the Disco browser.
xmpp = Disco(args.jid, args.password, args.target_jid, args.node, args.query) 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process(forever=False) # 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.")

136
examples/download_avatars.py Executable file → Normal file
View File

@@ -1,21 +1,33 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz Copyright (C) 2012 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser import threading
from optparse import OptionParser
import slixmpp import sleekxmpp
from slixmpp.exceptions import XMPPError from sleekxmpp.exceptions import XMPPError
from slixmpp import asyncio
# 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
FILE_TYPES = { FILE_TYPES = {
@@ -25,29 +37,24 @@ FILE_TYPES = {
} }
class AvatarDownloader(slixmpp.ClientXMPP): class AvatarDownloader(sleekxmpp.ClientXMPP):
""" """
A basic script for downloading the avatars for a user's contacts. A basic script for downloading the avatars for a user's contacts.
""" """
def __init__(self, jid, password): def __init__(self, jid, password):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start, threaded=True)
self.add_event_handler("changed_status", self.wait_for_presences) self.add_event_handler("changed_status", self.wait_for_presences)
self.add_event_handler('vcard_avatar_update', self.on_vcard_avatar) self.add_event_handler('vcard_avatar_update', self.on_vcard_avatar)
self.add_event_handler('avatar_metadata_publish', self.on_avatar) self.add_event_handler('avatar_metadata_publish', self.on_avatar)
self.received = set() self.received = set()
self.presences_received = asyncio.Event() self.presences_received = threading.Event()
self.roster_received = asyncio.Event()
def roster_received_cb(self, event): def start(self, event):
self.roster_received.set()
self.presences_received.clear()
async def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -61,19 +68,16 @@ class AvatarDownloader(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
self.get_roster(callback=self.roster_received_cb) self.get_roster()
print('Waiting for presence updates...\n') print('Waiting for presence updates...\n')
await self.roster_received.wait() self.presences_received.wait(15)
print('Roster received') self.disconnect(wait=True)
await self.presences_received.wait()
self.disconnect()
async def on_vcard_avatar(self, pres): def on_vcard_avatar(self, pres):
print("Received vCard avatar update from %s" % pres['from'].bare) print("Received vCard avatar update from %s" % pres['from'].bare)
try: try:
result = await self['xep_0054'].get_vcard(pres['from'].bare, cached=True, result = self['xep_0054'].get_vcard(pres['from'], cached=True)
timeout=5)
except XMPPError: except XMPPError:
print("Error retrieving avatar for %s" % pres['from']) print("Error retrieving avatar for %s" % pres['from'])
return return
@@ -84,17 +88,16 @@ class AvatarDownloader(slixmpp.ClientXMPP):
pres['from'].bare, pres['from'].bare,
pres['vcard_temp_update']['photo'], pres['vcard_temp_update']['photo'],
filetype) filetype)
with open(filename, 'wb+') as img: with open(filename, 'w+') as img:
img.write(avatar['BINVAL']) img.write(avatar['BINVAL'])
async def on_avatar(self, msg): def on_avatar(self, msg):
print("Received avatar update from %s" % msg['from']) print("Received avatar update from %s" % msg['from'])
metadata = msg['pubsub_event']['items']['item']['avatar_metadata'] metadata = msg['pubsub_event']['items']['item']['avatar_metadata']
for info in metadata['items']: for info in metadata['items']:
if not info['url']: if not info['url']:
try: try:
result = await self['xep_0084'].retrieve_avatar(msg['from'].bare, info['id'], result = self['xep_0084'].retrieve_avatar(msg['from'], info['id'])
timeout=5)
except XMPPError: except XMPPError:
print("Error retrieving avatar for %s" % msg['from']) print("Error retrieving avatar for %s" % msg['from'])
return return
@@ -103,7 +106,7 @@ class AvatarDownloader(slixmpp.ClientXMPP):
filetype = FILE_TYPES.get(metadata['type'], 'png') filetype = FILE_TYPES.get(metadata['type'], 'png')
filename = 'avatar_%s_%s.%s' % (msg['from'].bare, info['id'], filetype) filename = 'avatar_%s_%s.%s' % (msg['from'].bare, info['id'], filetype)
with open(filename, 'wb+') as img: with open(filename, 'w+') as img:
img.write(avatar['value']) img.write(avatar['value'])
else: else:
# We could retrieve the avatar via HTTP, etc here instead. # We could retrieve the avatar via HTTP, etc here instead.
@@ -114,7 +117,6 @@ class AvatarDownloader(slixmpp.ClientXMPP):
Wait to receive updates from all roster contacts. Wait to receive updates from all roster contacts.
""" """
self.received.add(pres['from'].bare) self.received.add(pres['from'].bare)
print((len(self.received), len(self.client_roster.keys())))
if len(self.received) >= len(self.client_roster.keys()): if len(self.received) >= len(self.client_roster.keys()):
self.presences_received.set() self.presences_received.set()
else: else:
@@ -123,40 +125,60 @@ class AvatarDownloader(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
parser.add_argument("-q","--quiet", help="set logging to ERROR", optp.add_option('-q','--quiet', help='set logging to ERROR',
action="store_const", action='store_const',
dest="loglevel", dest='loglevel',
const=logging.ERROR, const=logging.ERROR,
default=logging.ERROR) default=logging.ERROR)
parser.add_argument("-d","--debug", help="set logging to DEBUG", optp.add_option('-d','--debug', help='set logging to DEBUG',
action="store_const", action='store_const',
dest="loglevel", dest='loglevel',
const=logging.DEBUG, const=logging.DEBUG,
default=logging.ERROR) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
opts,args = optp.parse_args()
args = parser.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
xmpp = AvatarDownloader(args.jid, args.password) xmpp = AvatarDownloader(opts.jid, opts.password)
xmpp.register_plugin('xep_0054') xmpp.register_plugin('xep_0054')
xmpp.register_plugin('xep_0153') xmpp.register_plugin('xep_0153')
xmpp.register_plugin('xep_0084') xmpp.register_plugin('xep_0084')
# 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process(forever=False) # 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.")

View File

@@ -1,30 +1,41 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class EchoBot(slixmpp.ClientXMPP): class EchoBot(sleekxmpp.ClientXMPP):
""" """
A simple Slixmpp bot that will echo messages it A simple SleekXMPP bot that will echo messages it
receives, along with a short thank you message. receives, along with a short thank you message.
""" """
def __init__(self, jid, password): def __init__(self, jid, password):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The session_start event will be triggered when # The session_start event will be triggered when
# the bot establishes its connection with the server # the bot establishes its connection with the server
@@ -38,7 +49,7 @@ class EchoBot(slixmpp.ClientXMPP):
# MUC messages and error messages. # MUC messages and error messages.
self.add_event_handler("message", self.message) self.add_event_handler("message", self.message)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -52,7 +63,7 @@ class EchoBot(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
def message(self, msg): def message(self, msg):
""" """
@@ -72,42 +83,75 @@ class EchoBot(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser(description=EchoBot.__doc__) optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
# Setup the EchoBot and register plugins. Note that while plugins may # Setup the EchoBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = EchoBot(args.jid, args.password) xmpp = EchoBot(opts.jid, opts.password)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0060') # PubSub xmpp.register_plugin('xep_0060') # PubSub
xmpp.register_plugin('xep_0199') # XMPP Ping xmpp.register_plugin('xep_0199') # XMPP Ping
# If you are connecting to Facebook and wish to use the
# X-FACEBOOK-PLATFORM authentication mechanism, you will need
# your API key and an access token. Then you'll set:
# xmpp.credentials['api_key'] = 'THE_API_KEY'
# xmpp.credentials['access_token'] = 'THE_ACCESS_TOKEN'
# If you are connecting to MSN, then you will need an
# access token, and it does not matter what JID you
# specify other than that the domain is 'messenger.live.com',
# so '_@messenger.live.com' will work. You can specify
# the access token as so:
# xmpp.credentials['access_token'] = 'THE_ACCESS_TOKEN'
# 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() # 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.")

View File

@@ -1,26 +1,37 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp import sleekxmpp
from slixmpp.componentxmpp import ComponentXMPP 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class EchoComponent(ComponentXMPP): class EchoComponent(ComponentXMPP):
""" """
A simple Slixmpp component that echoes messages. A simple SleekXMPP component that echoes messages.
""" """
def __init__(self, jid, secret, server, port): def __init__(self, jid, secret, server, port):
@@ -56,50 +67,56 @@ class EchoComponent(ComponentXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser(description=EchoComponent.__doc__) optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
parser.add_argument("-s", "--server", dest="server", optp.add_option("-s", "--server", dest="server",
help="server to connect to") help="server to connect to")
parser.add_argument("-P", "--port", dest="port", optp.add_option("-P", "--port", dest="port",
help="port to connect to") help="port to connect to")
args = parser.parse_args() opts, args = optp.parse_args()
if args.jid is None: if opts.jid is None:
args.jid = input("Component JID: ") opts.jid = raw_input("Component JID: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
if args.server is None: if opts.server is None:
args.server = input("Server: ") opts.server = raw_input("Server: ")
if args.port is None: if opts.port is None:
args.port = int(input("Port: ")) opts.port = int(raw_input("Port: "))
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
# Setup the EchoComponent and register plugins. Note that while plugins # Setup the EchoComponent and register plugins. Note that while plugins
# may have interdependencies, the order in which you register them does # may have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = EchoComponent(args.jid, args.password, args.server, args.port) xmpp = EchoComponent(opts.jid, opts.password, opts.server, opts.port)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.registerPlugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms xmpp.registerPlugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0060') # PubSub xmpp.registerPlugin('xep_0060') # PubSub
xmpp.register_plugin('xep_0199') # XMPP Ping xmpp.registerPlugin('xep_0199') # XMPP Ping
# Connect to the XMPP server and start processing XMPP stanzas. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -1,34 +1,46 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp import sleekxmpp
import ssl import ssl
from slixmpp.xmlstream import cert from sleekxmpp.xmlstream import cert
class GTalkBot(slixmpp.ClientXMPP): # 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class GTalkBot(sleekxmpp.ClientXMPP):
""" """
A demonstration of using Slixmpp with accounts from a Google Apps A demonstration of using SleekXMPP with accounts from a Google Apps
account with a custom domain, because it requires custom certificate account with a custom domain, because it requires custom certificate
validation. validation.
""" """
def __init__(self, jid, password): def __init__(self, jid, password):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The session_start event will be triggered when # The session_start event will be triggered when
# the bot establishes its connection with the server # the bot establishes its connection with the server
@@ -55,10 +67,10 @@ class GTalkBot(slixmpp.ClientXMPP):
cert.verify('talk.google.com', der_cert) cert.verify('talk.google.com', der_cert)
logging.debug("CERT: Found GTalk certificate") logging.debug("CERT: Found GTalk certificate")
except cert.CertificateError as err: except cert.CertificateError as err:
logging.error(err.message) log.error(err.message)
self.disconnect() self.disconnect(send_close=False)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -72,7 +84,7 @@ class GTalkBot(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
def message(self, msg): def message(self, msg):
""" """
@@ -92,42 +104,62 @@ class GTalkBot(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
# Setup the GTalkBot and register plugins. Note that while plugins may # Setup the GTalkBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = GTalkBot(args.jid, args.password) xmpp = GTalkBot(opts.jid, opts.password)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0060') # PubSub xmpp.register_plugin('xep_0060') # PubSub
xmpp.register_plugin('xep_0199') # XMPP Ping 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() # 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.")

View File

@@ -1,97 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Implementation of HTTP over XMPP transport
http://xmpp.org/extensions/xep-0332.html
Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
This file is part of slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp import ClientXMPP
from argparse import ArgumentParser
import logging
import getpass
class HTTPOverXMPPClient(ClientXMPP):
def __init__(self, jid, password):
ClientXMPP.__init__(self, jid, password)
self.register_plugin('xep_0332') # HTTP over XMPP Transport
self.add_event_handler(
'session_start', self.session_start
)
self.add_event_handler('http_request', self.http_request_received)
self.add_event_handler('http_response', self.http_response_received)
def http_request_received(self, iq):
pass
def http_response_received(self, iq):
print('HTTP Response Received : %s' % iq)
print('From : %s' % iq['from'])
print('To : %s' % iq['to'])
print('Type : %s' % iq['type'])
print('Headers : %s' % iq['resp']['headers'])
print('Code : %s' % iq['resp']['code'])
print('Message : %s' % iq['resp']['message'])
print('Data : %s' % iq['resp']['data'])
def session_start(self, event):
# TODO: Fill in the blanks
self['xep_0332'].send_request(
to='?', method='?', resource='?', headers={}
)
self.disconnect()
if __name__ == '__main__':
#
# NOTE: To run this example, fill up the blanks in session_start() and
# use the following command.
#
# ./http_over_xmpp.py -J <jid> -P <pwd> -i <ip> -p <port> [-v]
#
parser = ArgumentParser()
# Output verbosity options.
parser.add_argument(
'-v', '--verbose', help='set logging to DEBUG', action='store_const',
dest='loglevel', const=logging.DEBUG, default=logging.ERROR
)
# JID and password options.
parser.add_argument('-J', '--jid', dest='jid', help='JID')
parser.add_argument('-P', '--password', dest='password', help='Password')
# XMPP server ip and port options.
parser.add_argument(
'-i', '--ipaddr', dest='ipaddr',
help='IP Address of the XMPP server', default=None
)
parser.add_argument(
'-p', '--port', dest='port',
help='Port of the XMPP server', default=None
)
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input('Username: ')
if args.password is None:
args.password = getpass.getpass('Password: ')
xmpp = HTTPOverXMPPClient(args.jid, args.password)
xmpp.connect()
xmpp.process()

View File

@@ -1,96 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2018 Emmanuel Gil Peyrot
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from getpass import getpass
from argparse import ArgumentParser
import slixmpp
log = logging.getLogger(__name__)
class HttpUpload(slixmpp.ClientXMPP):
"""
A basic client asking an entity if they confirm the access to an HTTP URL.
"""
def __init__(self, jid, password, recipient, filename, domain=None):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.recipient = recipient
self.filename = filename
self.domain = domain
self.add_event_handler("session_start", self.start)
async def start(self, event):
log.info('Uploading file %s...', self.filename)
def timeout_callback(arg):
raise TimeoutError("could not send message in time")
url = await self['xep_0363'].upload_file(
self.filename, domain=self.domain, timeout=10, timeout_callback=timeout_callback)
log.info('Upload success!')
log.info('Sending file to %s', self.recipient)
html = '<body xmlns="http://www.w3.org/1999/xhtml"><a href="%s">%s</a></body>' % (url, url)
self.send_message(self.recipient, url, mhtml=html)
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser()
parser.add_argument("-q","--quiet", help="set logging to ERROR",
action="store_const",
dest="loglevel",
const=logging.ERROR,
default=logging.INFO)
parser.add_argument("-d","--debug", help="set logging to DEBUG",
action="store_const",
dest="loglevel",
const=logging.DEBUG,
default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
# Other options.
parser.add_argument("-r", "--recipient", required=True,
help="Recipient JID")
parser.add_argument("-f", "--file", required=True,
help="File to send")
parser.add_argument("--domain",
help="Domain to use for HTTP File Upload (leave out for your own servers)")
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
xmpp = HttpUpload(args.jid, args.password, args.recipient, args.file, args.domain)
xmpp.register_plugin('xep_0071')
xmpp.register_plugin('xep_0128')
xmpp.register_plugin('xep_0363')
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process(forever=False)

View File

@@ -1,31 +1,45 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class IBBReceiver(slixmpp.ClientXMPP): class IBBReceiver(sleekxmpp.ClientXMPP):
""" """
A basic example of creating and using an in-band bytestream. A basic example of creating and using an in-band bytestream.
""" """
def __init__(self, jid, password, filename): def __init__(self, jid, password):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.file = open(filename, 'wb') self.register_plugin('xep_0030') # Service Discovery
self.register_plugin('xep_0047', {
'accept_stream': self.accept_stream
}) # In-band Bytestreams
# The session_start event will be triggered when # The session_start event will be triggered when
# the bot establishes its connection with the server # the bot establishes its connection with the server
@@ -36,7 +50,6 @@ class IBBReceiver(slixmpp.ClientXMPP):
self.add_event_handler("ibb_stream_start", self.stream_opened) self.add_event_handler("ibb_stream_start", self.stream_opened)
self.add_event_handler("ibb_stream_data", self.stream_data) self.add_event_handler("ibb_stream_data", self.stream_data)
self.add_event_handler("ibb_stream_end", self.stream_closed)
def start(self, event): def start(self, event):
""" """
@@ -54,59 +67,83 @@ class IBBReceiver(slixmpp.ClientXMPP):
self.send_presence() self.send_presence()
self.get_roster() self.get_roster()
def accept_stream(self, iq):
"""
Check that it is ok to accept a stream request.
Controlling stream acceptance can be done via either:
- setting 'auto_accept' to False in the plugin
configuration. The default is True.
- setting 'accept_stream' to a function which accepts
an Iq stanza as its argument, like this one.
The accept_stream function will be used if it exists, and the
auto_accept value will be used otherwise.
"""
return True
def stream_opened(self, stream): def stream_opened(self, stream):
print('Stream opened: %s from %s' % (stream.sid, stream.peer_jid)) # NOTE: IBB streams are bi-directional, so the original sender is
# now the opened stream's receiver.
print('Stream opened: %s from %s' % (stream.sid, stream.receiver))
def stream_data(self, stream): # You could run a loop reading from the stream using stream.recv(),
self.file.write(stream.read()) # or use the ibb_stream_data event.
def stream_closed(self, stream): def stream_data(self, event):
print('Stream closed: %s from %s' % (stream.sid, stream.peer_jid)) print(event['data'])
self.file.close()
self.disconnect()
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
parser.add_argument("-o", "--out", dest="filename",
help="file to save to")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
if args.filename is None:
args.filename = input("File path: ")
# Setup the IBBReceiver and register plugins. Note that while plugins may xmpp = IBBReceiver(opts.jid, opts.password)
# have interdependencies, the order in which you register them does
# not matter. # If you are working with an OpenFire server, you may need
xmpp = IBBReceiver(args.jid, args.password, args.filename) # to adjust the SSL version used:
xmpp.register_plugin('xep_0030') # Service Discovery # xmpp.ssl_version = ssl.PROTOCOL_SSLv3
xmpp.register_plugin('xep_0047', {
'auto_accept': True # If you want to verify the SSL certificates offered by a server:
}) # In-band Bytestreams # xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process(forever=False) # 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.")

View File

@@ -1,35 +1,43 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp import sleekxmpp
from slixmpp.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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class IBBSender(slixmpp.ClientXMPP): class IBBSender(sleekxmpp.ClientXMPP):
""" """
A basic example of creating and using an in-band bytestream. A basic example of creating and using an in-band bytestream.
""" """
def __init__(self, jid, password, receiver, filename, use_messages=False): def __init__(self, jid, password, receiver, filename):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.receiver = receiver self.receiver = receiver
self.filename = filename
self.file = open(filename, 'rb')
self.use_messages = use_messages
# The session_start event will be triggered when # The session_start event will be triggered when
# the bot establishes its connection with the server # the bot establishes its connection with the server
@@ -38,7 +46,7 @@ class IBBSender(slixmpp.ClientXMPP):
# our roster. # our roster.
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -54,70 +62,84 @@ class IBBSender(slixmpp.ClientXMPP):
self.send_presence() self.send_presence()
self.get_roster() self.get_roster()
try: # For the purpose of demonstration, we'll set a very small block
# Open the IBB stream in which to write to. # size. The default block size is 4096. We'll also use a window
stream = await self['xep_0047'].open_stream(self.receiver, use_messages=self.use_messages) # allowing sending multiple blocks at a time; in this case, three
# block transfers may be in progress at any time.
stream = self['xep_0047'].open_stream(self.receiver)
# If you want to send in-memory bytes, use stream.sendall() instead. with open(self.filename) as f:
await stream.sendfile(self.file, timeout=10) data = f.read()
stream.sendall(data)
# And finally close the stream.
await stream.close(timeout=10)
except (IqError, IqTimeout):
print('File transfer errored')
else:
print('File transfer finished')
finally:
self.file.close()
self.disconnect()
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
parser.add_argument("-r", "--receiver", dest="receiver", optp.add_option("-r", "--receiver", dest="receiver",
help="JID of the receiver") help="JID to use")
parser.add_argument("-f", "--file", dest="filename", optp.add_option("-f", "--file", dest="filename",
help="file to send") help="JID to use")
parser.add_argument("-m", "--use-messages", action="store_true",
help="use messages instead of iqs for file transfer")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
if args.receiver is None: if opts.receiver is None:
args.receiver = input("Receiver: ") opts.receiver = raw_input("Receiver: ")
if args.filename is None: if opts.filename is None:
args.filename = input("File path: ") opts.filename = raw_input("File path: ")
# Setup the IBBSender and register plugins. Note that while plugins may # Setup the EchoBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = IBBSender(args.jid, args.password, args.receiver, args.filename, args.use_messages) xmpp = IBBSender(opts.jid, opts.password, opts.receiver, opts.filename)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0047') # In-band Bytestreams xmpp.register_plugin('xep_0047') # In-band Bytestreams
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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process(forever=False) # 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.")

View File

@@ -1,97 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2017 Mathieu Pasquet
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from getpass import getpass
from argparse import ArgumentParser
import slixmpp
from slixmpp.exceptions import XMPPError
log = logging.getLogger(__name__)
class MAM(slixmpp.ClientXMPP):
"""
A basic client fetching mam archive messages
"""
def __init__(self, jid, password, remote_jid, start):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.remote_jid = remote_jid
self.start_date = start
self.add_event_handler("session_start", self.start)
async def start(self, *args):
"""
Fetch mam results for the specified JID.
Use RSM to paginate the results.
"""
results = self.plugin['xep_0313'].retrieve(jid=self.remote_jid, iterator=True, rsm={'max': 10}, start=self.start_date)
page = 1
async for rsm in results:
print('Page %d' % page)
for msg in rsm['mam']['results']:
forwarded = msg['mam_result']['forwarded']
timestamp = forwarded['delay']['stamp']
message = forwarded['stanza']
print('[%s] %s: %s' % (timestamp, message['from'], message['body']))
page += 1
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser()
parser.add_argument("-q","--quiet", help="set logging to ERROR",
action="store_const",
dest="loglevel",
const=logging.ERROR,
default=logging.INFO)
parser.add_argument("-d","--debug", help="set logging to DEBUG",
action="store_const",
dest="loglevel",
const=logging.DEBUG,
default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
# Other options
parser.add_argument("-r", "--remote-jid", dest="remote_jid",
help="Remote JID")
parser.add_argument("--start", help="Start date", default='2017-09-20T12:00:00Z')
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
if args.remote_jid is None:
args.remote_jid = input("Remote JID: ")
if args.start is None:
args.start = input("Start time: ")
xmpp = MAM(args.jid, args.password, args.remote_jid, args.start)
xmpp.register_plugin('xep_0313')
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process(forever=False)

View File

@@ -1,120 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from getpass import getpass
from argparse import ArgumentParser
import slixmpp
from slixmpp.plugins.xep_0394 import stanza as markup_stanza
class EchoBot(slixmpp.ClientXMPP):
"""
A simple Slixmpp bot that will echo messages it
receives, along with a short thank you message.
"""
def __init__(self, jid, password):
slixmpp.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)
async 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()
await 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.
"""
body = msg['body']
new_body = self['xep_0394'].to_plain_text(body, msg['markup'])
xhtml = self['xep_0394'].to_xhtml_im(body, msg['markup'])
print('Plain text:', new_body)
print('XHTML-IM:', xhtml['body'])
message = msg.reply()
message['body'] = new_body
message['html']['body'] = xhtml['body']
self.send(message)
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser(description=EchoBot.__doc__)
# Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR",
action="store_const", dest="loglevel",
const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG",
action="store_const", dest="loglevel",
const=logging.DEBUG, default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = 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(args.jid, args.password)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0199') # XMPP Ping
xmpp.register_plugin('xep_0394') # Message Markup
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process()

View File

@@ -1,107 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import logging
from getpass import getpass
from argparse import ArgumentParser
import slixmpp
# Setup the command line arguments.
parser = ArgumentParser()
# Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR",
action="store_const", dest="loglevel",
const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG",
action="store_const", dest="loglevel",
const=logging.DEBUG, default=logging.INFO)
# JID and password options.
parser.add_argument("--oldjid", dest="old_jid",
help="JID of the old account")
parser.add_argument("--oldpassword", dest="old_password",
help="password of the old account")
parser.add_argument("--newjid", dest="new_jid",
help="JID of the old account")
parser.add_argument("--newpassword", dest="new_password",
help="password of the old account")
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.old_jid is None:
args.old_jid = input("Old JID: ")
if args.old_password is None:
args.old_password = getpass("Old Password: ")
if args.new_jid is None:
args.new_jid = input("New JID: ")
if args.new_password is None:
args.new_password = getpass("New Password: ")
old_xmpp = slixmpp.ClientXMPP(args.old_jid, args.old_password)
# If you are connecting to Facebook and wish to use the
# X-FACEBOOK-PLATFORM authentication mechanism, you will need
# your API key and an access token. Then you'll set:
# xmpp.credentials['api_key'] = 'THE_API_KEY'
# xmpp.credentials['access_token'] = 'THE_ACCESS_TOKEN'
# If you are connecting to MSN, then you will need an
# access token, and it does not matter what JID you
# specify other than that the domain is 'messenger.live.com',
# so '_@messenger.live.com' will work. You can specify
# the access token as so:
# xmpp.credentials['access_token'] = 'THE_ACCESS_TOKEN'
# 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"
roster = []
def on_session(event):
roster.append(old_xmpp.get_roster())
old_xmpp.disconnect()
old_xmpp.add_event_handler('session_start', on_session)
if old_xmpp.connect():
old_xmpp.process(block=True)
if not roster:
print('No roster to migrate')
sys.exit()
new_xmpp = slixmpp.ClientXMPP(args.new_jid, args.new_password)
def on_session2(event):
new_xmpp.get_roster()
new_xmpp.send_presence()
logging.info(roster[0])
data = roster[0]['roster']['items']
logging.info(data)
for jid, item in data.items():
if item['subscription'] != 'none':
new_xmpp.send_presence(ptype='subscribe', pto=jid)
new_xmpp.update_roster(jid,
name = item['name'],
groups = item['groups'])
new_xmpp.disconnect()
new_xmpp.add_event_handler('session_start', on_session2)
new_xmpp.connect()
new_xmpp.process(forever=False)

View File

@@ -1,31 +1,42 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class MUCBot(slixmpp.ClientXMPP): class MUCBot(sleekxmpp.ClientXMPP):
""" """
A simple Slixmpp bot that will greets those A simple SleekXMPP bot that will greets those
who enter the room, and acknowledge any messages who enter the room, and acknowledge any messages
that mentions the bot's nickname. that mentions the bot's nickname.
""" """
def __init__(self, jid, password, room, nick): def __init__(self, jid, password, room, nick):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.room = room self.room = room
self.nick = nick self.nick = nick
@@ -52,7 +63,7 @@ class MUCBot(slixmpp.ClientXMPP):
self.muc_online) self.muc_online)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -65,13 +76,13 @@ class MUCBot(slixmpp.ClientXMPP):
event does not provide any additional event does not provide any additional
data. data.
""" """
await self.get_roster() self.get_roster()
self.send_presence() self.send_presence()
self.plugin['xep_0045'].join_muc(self.room, self.plugin['xep_0045'].joinMUC(self.room,
self.nick, self.nick,
# If a room password is needed, use: # If a room password is needed, use:
# password=the_room_password, # password=the_room_password,
wait=True) wait=True)
def muc_message(self, msg): def muc_message(self, msg):
""" """
@@ -121,49 +132,62 @@ class MUCBot(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
parser.add_argument("-r", "--room", dest="room", optp.add_option("-r", "--room", dest="room",
help="MUC room to join") help="MUC room to join")
parser.add_argument("-n", "--nick", dest="nick", optp.add_option("-n", "--nick", dest="nick",
help="MUC nickname") help="MUC nickname")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
if args.room is None: if opts.room is None:
args.room = input("MUC room: ") opts.room = raw_input("MUC room: ")
if args.nick is None: if opts.nick is None:
args.nick = input("MUC nickname: ") opts.nick = raw_input("MUC nickname: ")
# Setup the MUCBot and register plugins. Note that while plugins may # Setup the MUCBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = MUCBot(args.jid, args.password, args.room, args.nick) xmpp = MUCBot(opts.jid, opts.password, opts.room, opts.nick)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0045') # Multi-User Chat xmpp.register_plugin('xep_0045') # Multi-User Chat
xmpp.register_plugin('xep_0199') # XMPP Ping xmpp.register_plugin('xep_0199') # XMPP Ping
# Connect to the XMPP server and start processing XMPP stanzas. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() # 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.")

View File

@@ -1,33 +1,43 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
from slixmpp.exceptions import IqError, IqTimeout
import slixmpp 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class PingTest(slixmpp.ClientXMPP): class PingTest(sleekxmpp.ClientXMPP):
""" """
A simple Slixmpp bot that will send a ping request A simple SleekXMPP bot that will send a ping request
to a given JID. to a given JID.
""" """
def __init__(self, jid, password, pingjid): def __init__(self, jid, password, pingjid):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
if pingjid is None: if pingjid is None:
pingjid = self.boundjid.bare pingjid = self.jid
self.pingjid = pingjid self.pingjid = pingjid
# The session_start event will be triggered when # The session_start event will be triggered when
@@ -35,9 +45,9 @@ class PingTest(slixmpp.ClientXMPP):
# and the XML streams are ready for use. We want to # and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize # listen for this event so that we we can initialize
# our roster. # our roster.
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start, threaded=True)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -51,62 +61,81 @@ class PingTest(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
result = self['xep_0199'].send_ping(self.pingjid,
try: timeout=10,
rtt = await self['xep_0199'].ping(self.pingjid, errorfalse=True)
timeout=10) logging.info("Pinging...")
logging.info("Success! RTT: %s", rtt) if result is False:
except IqError as e: logging.info("Couldn't ping.")
logging.info("Error pinging %s: %s", self.disconnect()
self.pingjid, sys.exit(1)
e.iq['error']['condition']) else:
except IqTimeout: logging.info("Success! RTT: %s", str(result))
logging.info("No response from %s", self.pingjid)
finally:
self.disconnect() self.disconnect()
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) const=logging.DEBUG, default=logging.INFO)
parser.add_argument("-t", "--pingto", help="set jid to ping", optp.add_option('-v', '--verbose', help='set logging to COMM',
dest="pingjid", default=None) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
# Setup the PingTest and register plugins. Note that while plugins may # Setup the PingTest and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = PingTest(args.jid, args.password, args.pingjid) xmpp = PingTest(opts.jid, opts.password, opts.pingjid)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0060') # PubSub xmpp.register_plugin('xep_0060') # PubSub
xmpp.register_plugin('xep_0199') # XMPP Ping 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process(forever=False) # 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.")

View File

@@ -1,30 +1,41 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class EchoBot(slixmpp.ClientXMPP): class EchoBot(sleekxmpp.ClientXMPP):
""" """
A simple Slixmpp bot that will echo messages it A simple SleekXMPP bot that will echo messages it
receives, along with a short thank you message. receives, along with a short thank you message.
""" """
def __init__(self, jid, password): def __init__(self, jid, password):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The session_start event will be triggered when # The session_start event will be triggered when
# the bot establishes its connection with the server # the bot establishes its connection with the server
@@ -38,7 +49,7 @@ class EchoBot(slixmpp.ClientXMPP):
# MUC messages and error messages. # MUC messages and error messages.
self.add_event_handler("message", self.message) self.add_event_handler("message", self.message)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -52,7 +63,7 @@ class EchoBot(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
def message(self, msg): def message(self, msg):
""" """
@@ -71,65 +82,87 @@ class EchoBot(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
parser.add_argument("--phost", dest="proxy_host", optp.add_option("--phost", dest="proxy_host",
help="Proxy hostname") help="Proxy hostname")
parser.add_argument("--pport", dest="proxy_port", optp.add_option("--pport", dest="proxy_port",
help="Proxy port") help="Proxy port")
parser.add_argument("--puser", dest="proxy_user", optp.add_option("--puser", dest="proxy_user",
help="Proxy username") help="Proxy username")
parser.add_argument("--ppass", dest="proxy_pass", optp.add_option("--ppass", dest="proxy_pass",
help="Proxy password") help="Proxy password")
args = parser.parse_args()
opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
if args.proxy_host is None: if opts.proxy_host is None:
args.proxy_host = input("Proxy host: ") opts.proxy_host = raw_input("Proxy host: ")
if args.proxy_port is None: if opts.proxy_port is None:
args.proxy_port = input("Proxy port: ") opts.proxy_port = raw_input("Proxy port: ")
if args.proxy_user is None: if opts.proxy_user is None:
args.proxy_user = input("Proxy username: ") opts.proxy_user = raw_input("Proxy username: ")
if args.proxy_pass is None and args.proxy_user: if opts.proxy_pass is None and opts.proxy_user:
args.proxy_pass = getpass("Proxy password: ") opts.proxy_pass = getpass.getpass("Proxy password: ")
# Setup the EchoBot and register plugins. Note that while plugins may # Setup the EchoBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = EchoBot(args.jid, args.password) xmpp = EchoBot(opts.jid, opts.password)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0060') # PubSub xmpp.register_plugin('xep_0060') # PubSub
xmpp.register_plugin('xep_0199') # XMPP Ping 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.use_proxy = True
xmpp.proxy_config = { xmpp.proxy_config = {
'host': args.proxy_host, 'host': opts.proxy_host,
'port': int(args.proxy_port), 'port': int(opts.proxy_port),
'username': args.proxy_user, 'username': opts.proxy_user,
'password': args.proxy_pass} 'password': opts.proxy_pass}
# Connect to the XMPP server and start processing XMPP stanzas. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() # 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.")

243
examples/pubsub_client.py Executable file → Normal file
View File

@@ -1,26 +1,34 @@
#!/usr/bin/env python3 import sys
# -*- coding: utf-8 -*-
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp import sleekxmpp
from slixmpp.exceptions import XMPPError from sleekxmpp.xmlstream import ET, tostring
from slixmpp.xmlstream import ET, tostring
class PubsubClient(slixmpp.ClientXMPP): # 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
def __init__(self, jid, password, server,
node=None, action='nodes', data=''): class PubsubClient(sleekxmpp.ClientXMPP):
super().__init__(jid, password)
def __init__(self, jid, password, server,
node=None, action='list', data=''):
super(PubsubClient, self).__init__(jid, password)
self.register_plugin('xep_0030') self.register_plugin('xep_0030')
self.register_plugin('xep_0059') self.register_plugin('xep_0059')
self.register_plugin('xep_0060') self.register_plugin('xep_0060')
self.actions = ['nodes', 'create', 'delete', 'get_configure', self.actions = ['nodes', 'create', 'delete',
'publish', 'get', 'retract', 'publish', 'get', 'retract',
'purge', 'subscribe', 'unsubscribe'] 'purge', 'subscribe', 'unsubscribe']
@@ -29,143 +37,162 @@ class PubsubClient(slixmpp.ClientXMPP):
self.data = data self.data = data
self.pubsub_server = server self.pubsub_server = server
self.add_event_handler('session_start', self.start) self.add_event_handler('session_start', self.start, threaded=True)
async def start(self, event): def start(self, event):
await self.get_roster() self.get_roster()
self.send_presence() self.send_presence()
try: try:
await getattr(self, self.action)() getattr(self, self.action)()
except: except:
logging.exception('Could not execute %s:', self.action) logging.error('Could not execute: %s' % self.action)
self.disconnect() self.disconnect()
async def nodes(self): def nodes(self):
try: try:
result = await self['xep_0060'].get_nodes(self.pubsub_server, self.node) result = self['xep_0060'].get_nodes(self.pubsub_server, self.node)
for item in result['disco_items']['items']: for item in result['disco_items']['items']:
logging.info(' - %s', str(item)) print(' - %s' % str(item))
except XMPPError as error: except:
logging.error('Could not retrieve node list: %s', error.format()) logging.error('Could not retrieve node list.')
async def create(self): def create(self):
try: try:
await self['xep_0060'].create_node(self.pubsub_server, self.node) self['xep_0060'].create_node(self.pubsub_server, self.node)
logging.info('Created node %s', self.node) except:
except XMPPError as error: logging.error('Could not create node: %s' % self.node)
logging.error('Could not create node %s: %s', self.node, error.format())
async def delete(self): def delete(self):
try: try:
await self['xep_0060'].delete_node(self.pubsub_server, self.node) self['xep_0060'].delete_node(self.pubsub_server, self.node)
logging.info('Deleted node %s', self.node) print('Deleted node: %s' % self.node)
except XMPPError as error: except:
logging.error('Could not delete node %s: %s', self.node, error.format()) logging.error('Could not delete node: %s' % self.node)
async def get_configure(self): def publish(self):
try:
configuration_form = await self['xep_0060'].get_node_config(self.pubsub_server, self.node)
logging.info('Configure form received from node %s: %s', self.node, configuration_form['pubsub_owner']['configure']['form'])
except XMPPError as error:
logging.error('Could not retrieve configure form from node %s: %s', self.node, error.format())
async def publish(self):
payload = ET.fromstring("<test xmlns='test'>%s</test>" % self.data) payload = ET.fromstring("<test xmlns='test'>%s</test>" % self.data)
try: try:
result = await self['xep_0060'].publish(self.pubsub_server, self.node, payload=payload) result = self['xep_0060'].publish(self.pubsub_server, self.node, payload=payload)
logging.info('Published at item id: %s', result['pubsub']['publish']['item']['id']) id = result['pubsub']['publish']['item']['id']
except XMPPError as error: print('Published at item id: %s' % id)
logging.error('Could not publish to %s: %s', self.node, error.format()) except:
logging.error('Could not publish to: %s' % self.node)
async def get(self): def get(self):
try: try:
result = await self['xep_0060'].get_item(self.pubsub_server, self.node, self.data) result = self['xep_0060'].get_item(self.pubsub_server, self.node, self.data)
for item in result['pubsub']['items']['substanzas']: for item in result['pubsub']['items']['substanzas']:
logging.info('Retrieved item %s: %s', item['id'], tostring(item['payload'])) print('Retrieved item %s: %s' % (item['id'], tostring(item['payload'])))
except XMPPError as error: except:
logging.error('Could not retrieve item %s from node %s: %s', self.data, self.node, error.format()) logging.error('Could not retrieve item %s from node %s' % (self.data, self.node))
async def retract(self): def retract(self):
try: try:
await self['xep_0060'].retract(self.pubsub_server, self.node, self.data) result = self['xep_0060'].retract(self.pubsub_server, self.node, self.data)
logging.info('Retracted item %s from node %s', self.data, self.node) print('Retracted item %s from node %s' % (self.data, self.node))
except XMPPError as error: except:
logging.error('Could not retract item %s from node %s: %s', self.data, self.node, error.format()) logging.error('Could not retract item %s from node %s' % (self.data, self.node))
async def purge(self): def purge(self):
try: try:
await self['xep_0060'].purge(self.pubsub_server, self.node) result = self['xep_0060'].purge(self.pubsub_server, self.node)
logging.info('Purged all items from node %s', self.node) print('Purged all items from node %s' % self.node)
except XMPPError as error: except:
logging.error('Could not purge items from node %s: %s', self.node, error.format()) logging.error('Could not purge items from node %s' % self.node)
async def subscribe(self): def subscribe(self):
try: try:
iq = await self['xep_0060'].subscribe(self.pubsub_server, self.node) result = self['xep_0060'].subscribe(self.pubsub_server, self.node)
subscription = iq['pubsub']['subscription'] print('Subscribed %s to node %s' % (self.boundjid.bare, self.node))
logging.info('Subscribed %s to node %s', subscription['jid'], subscription['node']) except:
except XMPPError as error: logging.error('Could not subscribe %s to node %s' % (self.boundjid.bare, self.node))
logging.error('Could not subscribe %s to node %s: %s', self.boundjid.bare, self.node, error.format())
async def unsubscribe(self): def unsubscribe(self):
try: try:
await self['xep_0060'].unsubscribe(self.pubsub_server, self.node) result = self['xep_0060'].unsubscribe(self.pubsub_server, self.node)
logging.info('Unsubscribed %s from node %s', self.boundjid.bare, self.node) print('Unsubscribed %s from node %s' % (self.boundjid.bare, self.node))
except XMPPError as error: except:
logging.error('Could not unsubscribe %s from node %s: %s', self.boundjid.bare, self.node, error.format()) logging.error('Could not unsubscribe %s from node %s' % (self.boundjid.bare, self.node))
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
parser.version = '%%prog 0.1' optp.version = '%%prog 0.1'
parser.usage = "Usage: %%prog [options] <jid> " + \ optp.usage = "Usage: %%prog [options] <jid> " + \
'nodes|create|delete|get_configure|purge|subscribe|unsubscribe|publish|retract|get' + \ 'nodes|create|delete|purge|subscribe|unsubscribe|publish|retract|get' + \
' [<node> <data>]' ' [<node> <data>]'
parser.add_argument("-q","--quiet", help="set logging to ERROR", optp.add_option('-q','--quiet', help='set logging to ERROR',
action="store_const", action='store_const',
dest="loglevel", dest='loglevel',
const=logging.ERROR, const=logging.ERROR,
default=logging.INFO) default=logging.ERROR)
parser.add_argument("-d","--debug", help="set logging to DEBUG", optp.add_option('-d','--debug', help='set logging to DEBUG',
action="store_const", action='store_const',
dest="loglevel", dest='loglevel',
const=logging.DEBUG, const=logging.DEBUG,
default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
opts,args = optp.parse_args()
parser.add_argument("server")
parser.add_argument("action", choices=["nodes", "create", "delete", "get_configure", "purge", "subscribe", "unsubscribe", "publish", "retract", "get"])
parser.add_argument("node", nargs='?')
parser.add_argument("data", nargs='?')
args = parser.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if len(args) < 2:
args.jid = input("Username: ") optp.print_help()
if args.password is None: exit()
args.password = getpass("Password: ")
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
if len(args) == 2:
args = (args[0], args[1], '', '', '')
elif len(args) == 3:
args = (args[0], args[1], args[2], '', '')
elif len(args) == 4:
args = (args[0], args[1], args[2], args[3], '')
# Setup the Pubsub client # Setup the Pubsub client
xmpp = PubsubClient(args.jid, args.password, xmpp = PubsubClient(opts.jid, opts.password,
server=args.server, server=args[0],
node=args.node, node=args[2],
action=args.action, action=args[1],
data=args.data) data=args[3])
# 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process(forever=False) # 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.")

97
examples/pubsub_events.py Executable file → Normal file
View File

@@ -1,20 +1,29 @@
#!/usr/bin/env python3 import sys
# -*- coding: utf-8 -*-
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp import sleekxmpp
from slixmpp.xmlstream import ET, tostring from sleekxmpp.xmlstream import ET, tostring
from slixmpp.xmlstream.matcher import StanzaPath from sleekxmpp.xmlstream.matcher import StanzaPath
from slixmpp.xmlstream.handler import Callback from sleekxmpp.xmlstream.handler import Callback
class PubsubEvents(slixmpp.ClientXMPP): # 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class PubsubEvents(sleekxmpp.ClientXMPP):
def __init__(self, jid, password): def __init__(self, jid, password):
super().__init__(jid, password) super(PubsubEvents, self).__init__(jid, password)
self.register_plugin('xep_0030') self.register_plugin('xep_0030')
self.register_plugin('xep_0059') self.register_plugin('xep_0059')
@@ -38,8 +47,8 @@ class PubsubEvents(slixmpp.ClientXMPP):
# self.add_event_handler('event_prefix_purge', handler) # self.add_event_handler('event_prefix_purge', handler)
# self.add_event_handler('event_prefix_delete', handler) # self.add_event_handler('event_prefix_delete', handler)
async def start(self, event): def start(self, event):
await self.get_roster() self.get_roster()
self.send_presence() self.send_presence()
def _publish(self, msg): def _publish(self, msg):
@@ -68,7 +77,7 @@ class PubsubEvents(slixmpp.ClientXMPP):
"""Handle receiving a node deletion event.""" """Handle receiving a node deletion event."""
print('Deleted node %s' % ( print('Deleted node %s' % (
msg['pubsub_event']['delete']['node'])) msg['pubsub_event']['delete']['node']))
def _config(self, msg): def _config(self, msg):
"""Handle receiving a node configuration event.""" """Handle receiving a node configuration event."""
print('Configured node %s:' % ( print('Configured node %s:' % (
@@ -84,39 +93,59 @@ class PubsubEvents(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
logging.info("Run this in conjunction with the pubsub_client.py " + \ logging.info("Run this in conjunction with the pubsub_client.py " + \
"example to watch events happen as you give commands.") "example to watch events happen as you give commands.")
# Setup the PubsubEvents listener # Setup the PubsubEvents listener
xmpp = PubsubEvents(args.jid, args.password) xmpp = PubsubEvents(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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() # 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.")

109
examples/register_account.py Executable file → Normal file
View File

@@ -1,23 +1,34 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp import sleekxmpp
from slixmpp.exceptions import IqError, IqTimeout 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class RegisterBot(slixmpp.ClientXMPP): class RegisterBot(sleekxmpp.ClientXMPP):
""" """
A basic bot that will attempt to register an account A basic bot that will attempt to register an account
@@ -29,25 +40,25 @@ class RegisterBot(slixmpp.ClientXMPP):
""" """
def __init__(self, jid, password): def __init__(self, jid, password):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The session_start event will be triggered when # The session_start event will be triggered when
# the bot establishes its connection with the server # the bot establishes its connection with the server
# and the XML streams are ready for use. We want to # and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize # listen for this event so that we we can initialize
# our roster. # our roster.
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start, threaded=True)
# The register event provides an Iq result stanza with # The register event provides an Iq result stanza with
# a registration form from the server. This may include # a registration form from the server. This may include
# the basic registration fields, a data form, an # the basic registration fields, a data form, an
# out-of-band URL, or any combination. For more advanced # out-of-band URL, or any combination. For more advanced
# cases, you will need to examine the fields provided # cases, you will need to examine the fields provided
# and respond accordingly. Slixmpp provides plugins # and respond accordingly. SleekXMPP provides plugins
# for data forms and OOB links that will make that easier. # for data forms and OOB links that will make that easier.
self.add_event_handler("register", self.register) self.add_event_handler("register", self.register, threaded=True)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -61,12 +72,12 @@ class RegisterBot(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
# We're only concerned about registering, so nothing more to do here. # We're only concerned about registering, so nothing more to do here.
self.disconnect() self.disconnect()
async def register(self, iq): def register(self, iq):
""" """
Fill out and submit a registration form. Fill out and submit a registration form.
@@ -90,10 +101,10 @@ class RegisterBot(slixmpp.ClientXMPP):
resp['register']['password'] = self.password resp['register']['password'] = self.password
try: try:
await resp.send() resp.send(now=True)
logging.info("Account created for %s!" % self.boundjid) logging.info("Account created for %s!" % self.boundjid)
except IqError as e: except IqError as e:
logging.error("Could not register account: %s" % logging.error("Could not register account: %s" %
e.iq['error']['text']) e.iq['error']['text'])
self.disconnect() self.disconnect()
except IqTimeout: except IqTimeout:
@@ -103,46 +114,62 @@ class RegisterBot(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
# Setup the RegisterBot and register plugins. Note that while plugins may # Setup the RegisterBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = RegisterBot(args.jid, args.password) xmpp = RegisterBot(opts.jid, opts.password)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data forms xmpp.register_plugin('xep_0004') # Data forms
xmpp.register_plugin('xep_0066') # Out-of-band Data xmpp.register_plugin('xep_0066') # Out-of-band Data
xmpp.register_plugin('xep_0077') # In-band Registration xmpp.register_plugin('xep_0077') # In-band Registration
# Some servers don't advertise support for inband registration, even # If you are working with an OpenFire server, you may need
# though they allow it. If this applies to your server, use: # to adjust the SSL version used:
xmpp['xep_0077'].force_registration = True # 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() # 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.")

119
examples/roster_browser.py Executable file → Normal file
View File

@@ -1,24 +1,36 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Nathanael C. Fritz Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser import threading
from optparse import OptionParser
import slixmpp import sleekxmpp
from slixmpp.exceptions import IqError, IqTimeout from sleekxmpp.exceptions import IqError, IqTimeout
from slixmpp.xmlstream.asyncio import asyncio
class RosterBrowser(slixmpp.ClientXMPP): # 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class RosterBrowser(sleekxmpp.ClientXMPP):
""" """
A basic script for dumping a client's roster to A basic script for dumping a client's roster to
@@ -26,19 +38,21 @@ class RosterBrowser(slixmpp.ClientXMPP):
""" """
def __init__(self, jid, password): def __init__(self, jid, password):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The session_start event will be triggered when # The session_start event will be triggered when
# the bot establishes its connection with the server # the bot establishes its connection with the server
# and the XML streams are ready for use. We want to # and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize # listen for this event so that we we can initialize
# our roster. # our roster. We need threaded=True so that the
self.add_event_handler("session_start", self.start) # 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.add_event_handler("changed_status", self.wait_for_presences)
self.received = set() self.received = set()
self.presences_received = asyncio.Event() self.presences_received = threading.Event()
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -52,16 +66,16 @@ class RosterBrowser(slixmpp.ClientXMPP):
data. data.
""" """
try: try:
await self.get_roster() self.get_roster()
except IqError as err: except IqError as err:
print('Error: %s' % err.iq['error']['condition']) print('Error: %' % err.iq['error']['condition'])
except IqTimeout: except IqTimeout:
print('Error: Request timed out') print('Error: Request timed out')
self.send_presence() self.send_presence()
print('Waiting for presence updates...\n') print('Waiting for presence updates...\n')
await asyncio.sleep(10) self.presences_received.wait(5)
print('Roster for %s' % self.boundjid.bare) print('Roster for %s' % self.boundjid.bare)
groups = self.client_roster.groups() groups = self.client_roster.groups()
@@ -101,37 +115,58 @@ class RosterBrowser(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
parser.add_argument("-q","--quiet", help="set logging to ERROR", optp.add_option('-q','--quiet', help='set logging to ERROR',
action="store_const", action='store_const',
dest="loglevel", dest='loglevel',
const=logging.ERROR, const=logging.ERROR,
default=logging.ERROR) default=logging.ERROR)
parser.add_argument("-d","--debug", help="set logging to DEBUG", optp.add_option('-d','--debug', help='set logging to DEBUG',
action="store_const", action='store_const',
dest="loglevel", dest='loglevel',
const=logging.DEBUG, const=logging.DEBUG,
default=logging.ERROR) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
opts,args = optp.parse_args()
args = parser.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
xmpp = RosterBrowser(args.jid, args.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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process(forever=False) # 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.")

37
examples/rpc_async.py Executable file → Normal file
View File

@@ -1,47 +1,44 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Dann Martens Copyright (C) 2011 Dann Martens
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
from slixmpp.plugins.xep_0009.remote import Endpoint, remote, Remote, \ from sleekxmpp.plugins.xep_0009.remote import Endpoint, remote, Remote, \
ANY_ALL, Future ANY_ALL, Future
import time import time
class Boomerang(Endpoint): class Boomerang(Endpoint):
def FQN(self): def FQN(self):
return 'boomerang' return 'boomerang'
@remote @remote
def throw(self): def throw(self):
print("Duck!") print "Duck!"
def main(): def main():
session = Remote.new_session('kangaroo@xmpp.org/rpc', '*****') session = Remote.new_session('kangaroo@xmpp.org/rpc', '*****')
session.new_handler(ANY_ALL, Boomerang) session.new_handler(ANY_ALL, Boomerang)
boomerang = session.new_proxy('kangaroo@xmpp.org/rpc', Boomerang) boomerang = session.new_proxy('kangaroo@xmpp.org/rpc', Boomerang)
callback = Future() callback = Future()
boomerang.async(callback).throw() boomerang.async(callback).throw()
time.sleep(10) time.sleep(10)
session.close() session.close()
if __name__ == '__main__': if __name__ == '__main__':
main() main()

35
examples/rpc_client_side.py Executable file → Normal file
View File

@@ -1,32 +1,29 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Dann Martens Copyright (C) 2011 Dann Martens
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
from slixmpp.plugins.xep_0009.remote import Endpoint, remote, Remote, \ from sleekxmpp.plugins.xep_0009.remote import Endpoint, remote, Remote, \
ANY_ALL ANY_ALL
import threading import threading
import time import time
class Thermostat(Endpoint): class Thermostat(Endpoint):
def FQN(self): def FQN(self):
return 'thermostat' return 'thermostat'
def __init__(self, initial_temperature): def __init__(self, initial_temperature):
self._temperature = initial_temperature self._temperature = initial_temperature
self._event = threading.Event() self._event = threading.Event()
@remote @remote
def set_temperature(self, temperature): def set_temperature(self, temperature):
return NotImplemented return NotImplemented
@remote @remote
def get_temperature(self): def get_temperature(self):
return NotImplemented return NotImplemented
@@ -34,23 +31,23 @@ class Thermostat(Endpoint):
@remote(False) @remote(False)
def release(self): def release(self):
return NotImplemented return NotImplemented
def main(): def main():
session = Remote.new_session('operator@xmpp.org/rpc', '*****') session = Remote.new_session('operator@xmpp.org/rpc', '*****')
thermostat = session.new_proxy('thermostat@xmpp.org/rpc', Thermostat) thermostat = session.new_proxy('thermostat@xmpp.org/rpc', Thermostat)
print("Current temperature is %s" % thermostat.get_temperature()) print("Current temperature is %s" % thermostat.get_temperature())
thermostat.set_temperature(20) thermostat.set_temperature(20)
time.sleep(10) time.sleep(10)
session.close() session.close()
if __name__ == '__main__': if __name__ == '__main__':
main() main()

37
examples/rpc_server_side.py Executable file → Normal file
View File

@@ -1,55 +1,52 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Dann Martens Copyright (C) 2011 Dann Martens
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
from slixmpp.plugins.xep_0009.remote import Endpoint, remote, Remote, \ from sleekxmpp.plugins.xep_0009.remote import Endpoint, remote, Remote, \
ANY_ALL ANY_ALL
import threading import threading
class Thermostat(Endpoint): class Thermostat(Endpoint):
def FQN(self): def FQN(self):
return 'thermostat' return 'thermostat'
def __init__(self, initial_temperature): def __init__(self, initial_temperature):
self._temperature = initial_temperature self._temperature = initial_temperature
self._event = threading.Event() self._event = threading.Event()
@remote @remote
def set_temperature(self, temperature): def set_temperature(self, temperature):
print("Setting temperature to %s" % temperature) print("Setting temperature to %s" % temperature)
self._temperature = temperature self._temperature = temperature
@remote @remote
def get_temperature(self): def get_temperature(self):
return self._temperature return self._temperature
@remote(False) @remote(False)
def release(self): def release(self):
self._event.set() self._event.set()
def wait_for_release(self): def wait_for_release(self):
self._event.wait() self._event.wait()
def main(): def main():
session = Remote.new_session('sleek@xmpp.org/rpc', '*****') session = Remote.new_session('sleek@xmpp.org/rpc', '*****')
thermostat = session.new_handler(ANY_ALL, Thermostat, 18) thermostat = session.new_handler(ANY_ALL, Thermostat, 18)
thermostat.wait_for_release() thermostat.wait_for_release()
session.close() session.close()
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@@ -1,89 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2015 Emmanuel Gil Peyrot
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from getpass import getpass
from argparse import ArgumentParser
import slixmpp
class S5BReceiver(slixmpp.ClientXMPP):
"""
A basic example of creating and using a SOCKS5 bytestream.
"""
def __init__(self, jid, password, filename):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.file = open(filename, 'wb')
self.add_event_handler("socks5_connected", self.stream_opened)
self.add_event_handler("socks5_data", self.stream_data)
self.add_event_handler("socks5_closed", self.stream_closed)
def stream_opened(self, sid):
logging.info('Stream opened. %s', sid)
def stream_data(self, data):
self.file.write(data)
def stream_closed(self, exception):
logging.info('Stream closed. %s', exception)
self.file.close()
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser()
# Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR",
action="store_const", dest="loglevel",
const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG",
action="store_const", dest="loglevel",
const=logging.DEBUG, default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
parser.add_argument("-o", "--out", dest="filename",
help="file to save to")
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
if args.filename is None:
args.filename = input("File path: ")
# Setup the S5BReceiver and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = S5BReceiver(args.jid, args.password, args.filename)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0065', {
'auto_accept': True
}) # SOCKS5 Bytestreams
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process(forever=False)

View File

@@ -1,122 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Copyright (C) 2015 Emmanuel Gil Peyrot
This file is part of Slixmpp.
See the file LICENSE for copying permission.
"""
import logging
from getpass import getpass
from argparse import ArgumentParser
import slixmpp
from slixmpp.exceptions import IqError, IqTimeout
class S5BSender(slixmpp.ClientXMPP):
"""
A basic example of creating and using a SOCKS5 bytestream.
"""
def __init__(self, jid, password, receiver, filename):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.receiver = receiver
self.file = open(filename, 'rb')
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use.
self.add_event_handler("session_start", self.start)
async 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:
# Open the S5B stream in which to write to.
proxy = await self['xep_0065'].handshake(self.receiver)
# Send the entire file.
while True:
data = self.file.read(1048576)
if not data:
break
await proxy.write(data)
# And finally close the stream.
proxy.transport.write_eof()
except (IqError, IqTimeout):
print('File transfer errored')
else:
print('File transfer finished')
finally:
self.file.close()
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser()
# Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR",
action="store_const", dest="loglevel",
const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG",
action="store_const", dest="loglevel",
const=logging.DEBUG, default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
parser.add_argument("-r", "--receiver", dest="receiver",
help="JID of the receiver")
parser.add_argument("-f", "--file", dest="filename",
help="file to send")
parser.add_argument("-m", "--use-messages", action="store_true",
help="use messages instead of iqs for file transfer")
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
if args.receiver is None:
args.receiver = input("Receiver: ")
if args.filename is None:
args.filename = input("File path: ")
# Setup the S5BSender and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = S5BSender(args.jid, args.password, args.receiver, args.filename)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0065') # SOCKS5 Bytestreams
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process(forever=False)

View File

@@ -1,30 +1,41 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class SendMsgBot(slixmpp.ClientXMPP): class SendMsgBot(sleekxmpp.ClientXMPP):
""" """
A basic Slixmpp bot that will log in, send a message, A basic SleekXMPP bot that will log in, send a message,
and then log out. and then log out.
""" """
def __init__(self, jid, password, recipient, message): def __init__(self, jid, password, recipient, message):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The message we wish to send, and the JID that # The message we wish to send, and the JID that
# will receive it. # will receive it.
@@ -36,9 +47,9 @@ class SendMsgBot(slixmpp.ClientXMPP):
# and the XML streams are ready for use. We want to # and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize # listen for this event so that we we can initialize
# our roster. # our roster.
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start, threaded=True)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -52,59 +63,81 @@ class SendMsgBot(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
self.send_message(mto=self.recipient, self.send_message(mto=self.recipient,
mbody=self.msg, mbody=self.msg,
mtype='chat') mtype='chat')
self.disconnect() # Using wait=True ensures that the send queue will be
# emptied before ending the session.
self.disconnect(wait=True)
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser(description=SendMsgBot.__doc__) optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
parser.add_argument("-t", "--to", dest="to", optp.add_option("-t", "--to", dest="to",
help="JID to send the message to") help="JID to send the message to")
parser.add_argument("-m", "--message", dest="message", optp.add_option("-m", "--message", dest="message",
help="message to send") help="message to send")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
if args.to is None: if opts.to is None:
args.to = input("Send To: ") opts.to = raw_input("Send To: ")
if args.message is None: if opts.message is None:
args.message = input("Message: ") opts.message = raw_input("Message: ")
# Setup the EchoBot and register plugins. Note that while plugins may # Setup the EchoBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does # have interdependencies, the order in which you register them does
# not matter. # not matter.
xmpp = SendMsgBot(args.jid, args.password, args.to, args.message) xmpp = SendMsgBot(opts.jid, opts.password, opts.to, opts.message)
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0199') # XMPP Ping 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process(forever=False) # 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.")

158
examples/set_avatar.py Executable file → Normal file
View File

@@ -1,38 +1,51 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2012 Nathanael C. Fritz Copyright (C) 2012 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import os import os
import sys
import imghdr import imghdr
import logging import logging
from getpass import getpass import getpass
import threading import threading
from argparse import ArgumentParser from optparse import OptionParser
import slixmpp import sleekxmpp
from slixmpp.exceptions import XMPPError from sleekxmpp.exceptions import XMPPError
class AvatarSetter(slixmpp.ClientXMPP):
# 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class AvatarSetter(sleekxmpp.ClientXMPP):
""" """
A basic script for downloading the avatars for a user's contacts. A basic script for downloading the avatars for a user's contacts.
""" """
def __init__(self, jid, password, filepath): def __init__(self, jid, password, filepath):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start, threaded=True)
self.filepath = filepath self.filepath = filepath
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -46,11 +59,11 @@ class AvatarSetter(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
avatar_file = None avatar_file = None
try: try:
avatar_file = open(os.path.expanduser(self.filepath), 'rb') avatar_file = open(os.path.expanduser(self.filepath))
except IOError: except IOError:
print('Could not find file: %s' % self.filepath) print('Could not find file: %s' % self.filepath)
return self.disconnect() return self.disconnect()
@@ -64,31 +77,32 @@ class AvatarSetter(slixmpp.ClientXMPP):
avatar_file.close() avatar_file.close()
used_xep84 = False used_xep84 = False
try:
print('Publish XEP-0084 avatar data') print('Publish XEP-0084 avatar data')
result = await self['xep_0084'].publish_avatar(avatar) self['xep_0084'].publish_avatar(avatar)
if isinstance(result, XMPPError):
print('Could not publish XEP-0084 avatar')
else:
used_xep84 = True used_xep84 = True
except XMPPError:
print('Could not publish XEP-0084 avatar')
print('Update vCard with avatar') try:
result = await self['xep_0153'].set_avatar(avatar=avatar, mtype=avatar_type) print('Update vCard with avatar')
if isinstance(result, XMPPError): self['xep_0153'].set_avatar(avatar=avatar, mtype=avatar_type)
except XMPPError:
print('Could not set vCard avatar') print('Could not set vCard avatar')
if used_xep84: if used_xep84:
print('Advertise XEP-0084 avatar metadata') try:
result = await self['xep_0084'].publish_avatar_metadata([ print('Advertise XEP-0084 avatar metadata')
{'id': avatar_id, self['xep_0084'].publish_avatar_metadata([
'type': avatar_type, {'id': avatar_id,
'bytes': avatar_bytes} 'type': avatar_type,
# We could advertise multiple avatars to provide 'bytes': avatar_bytes}
# options in image type, source (HTTP vs pubsub), # We could advertise multiple avatars to provide
# size, etc. # options in image type, source (HTTP vs pubsub),
# {'id': ....} # size, etc.
]) # {'id': ....}
if isinstance(result, XMPPError): ])
except XMPPError:
print('Could not publish XEP-0084 metadata') print('Could not publish XEP-0084 metadata')
print('Wait for presence updates to propagate...') print('Wait for presence updates to propagate...')
@@ -97,44 +111,64 @@ class AvatarSetter(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
parser.add_argument("-q","--quiet", help="set logging to ERROR", optp.add_option('-q','--quiet', help='set logging to ERROR',
action="store_const", action='store_const',
dest="loglevel", dest='loglevel',
const=logging.ERROR, const=logging.ERROR,
default=logging.ERROR) default=logging.ERROR)
parser.add_argument("-d","--debug", help="set logging to DEBUG", optp.add_option('-d','--debug', help='set logging to DEBUG',
action="store_const", action='store_const',
dest="loglevel", dest='loglevel',
const=logging.DEBUG, const=logging.DEBUG,
default=logging.ERROR) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
parser.add_argument("-f", "--file", dest="filepath", optp.add_option("-f", "--file", dest="filepath",
help="path to the avatar file") help="path to the avatar file")
opts,args = optp.parse_args()
args = parser.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
if args.filepath is None: if opts.filepath is None:
args.filepath = input("Avatar file location: ") opts.filepath = raw_input("Avatar file location: ")
xmpp = AvatarSetter(args.jid, args.password, args.filepath) xmpp = AvatarSetter(opts.jid, opts.password, opts.filepath)
xmpp.register_plugin('xep_0054') xmpp.register_plugin('xep_0054')
xmpp.register_plugin('xep_0153') xmpp.register_plugin('xep_0153')
xmpp.register_plugin('xep_0084') xmpp.register_plugin('xep_0084')
# 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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process(forever=False) # 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.")

101
examples/thirdparty_auth.py Executable file → Normal file
View File

@@ -1,18 +1,18 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import sys import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
try: try:
from httplib import HTTPSConnection from httplib import HTTPSConnection
@@ -21,14 +21,24 @@ except ImportError:
from urllib.parse import urlencode from urllib.parse import urlencode
from http.client import HTTPSConnection from http.client import HTTPSConnection
import slixmpp import sleekxmpp
from slixmpp.xmlstream import JID from sleekxmpp.xmlstream import JID
# 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):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
else:
raw_input = input
class ThirdPartyAuthBot(slixmpp.ClientXMPP): class ThirdPartyAuthBot(sleekxmpp.ClientXMPP):
""" """
A simple Slixmpp bot that will echo messages it A simple SleekXMPP bot that will echo messages it
receives, along with a short thank you message. receives, along with a short thank you message.
This version uses a thirdpary service for authentication, This version uses a thirdpary service for authentication,
@@ -36,7 +46,7 @@ class ThirdPartyAuthBot(slixmpp.ClientXMPP):
""" """
def __init__(self, jid, password): def __init__(self, jid, password):
slixmpp.ClientXMPP.__init__(self, jid, password) sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The X-GOOGLE-TOKEN mech is ranked lower than PLAIN # The X-GOOGLE-TOKEN mech is ranked lower than PLAIN
# due to Google only allowing a single SASL attempt per # due to Google only allowing a single SASL attempt per
@@ -45,7 +55,7 @@ class ThirdPartyAuthBot(slixmpp.ClientXMPP):
# X-GOOGLE-TOKEN with a TLS connection, explicitly select # X-GOOGLE-TOKEN with a TLS connection, explicitly select
# it using: # it using:
# #
# slixmpp.ClientXMPP.__init__(self, jid, password, # sleekxmpp.ClientXMPP.__init__(self, jid, password,
# sasl_mech="X-GOOGLE-TOKEN") # sasl_mech="X-GOOGLE-TOKEN")
# The session_start event will be triggered when # The session_start event will be triggered when
@@ -60,7 +70,7 @@ class ThirdPartyAuthBot(slixmpp.ClientXMPP):
# MUC messages and error messages. # MUC messages and error messages.
self.add_event_handler("message", self.message) self.add_event_handler("message", self.message)
async def start(self, event): def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -74,7 +84,7 @@ class ThirdPartyAuthBot(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
def message(self, msg): def message(self, msg):
""" """
@@ -94,34 +104,37 @@ class ThirdPartyAuthBot(slixmpp.ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
access_token = None access_token = None
# Since documentation on how to work with Google tokens # Since documentation on how to work with Google tokens
@@ -143,11 +156,11 @@ if __name__ == '__main__':
params = urlencode({ params = urlencode({
'accountType': 'GOOGLE', 'accountType': 'GOOGLE',
'service': 'mail', 'service': 'mail',
'Email': JID(args.jid).bare, 'Email': JID(opts.jid).bare,
'Passwd': args.password 'Passwd': opts.password
}) })
headers = { headers = {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
} }
try: try:
conn.request('POST', '/accounts/ClientLogin', params, headers) conn.request('POST', '/accounts/ClientLogin', params, headers)
@@ -195,12 +208,12 @@ if __name__ == '__main__':
# We're using an access token instead of a password, so we'll use `''` as # We're using an access token instead of a password, so we'll use `''` as
# a password argument filler. # a password argument filler.
xmpp = ThirdPartyAuthBot(args.jid, '') xmpp = ThirdPartyAuthBot(opts.jid, '')
xmpp.credentials['access_token'] = access_token xmpp.credentials['access_token'] = access_token
# The credentials dictionary is used to provide additional authentication # The credentials dictionary is used to provide additional authentication
# information beyond just a password. # information beyond just a password.
xmpp.register_plugin('xep_0030') # Service Discovery xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0060') # PubSub xmpp.register_plugin('xep_0060') # PubSub
@@ -218,7 +231,17 @@ if __name__ == '__main__':
# xmpp.ca_certs = "path/to/ca/cert" # xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas. # Connect to the XMPP server and start processing XMPP stanzas.
# Google only allows one SASL attempt per connection, so in order to # Google only allows one SASL attempt per connection, so in order to
# enable the X-GOOGLE-TOKEN mechanism, we'll disable TLS. # enable the X-GOOGLE-TOKEN mechanism, we'll disable TLS.
xmpp.connect() if xmpp.connect(use_tls=False):
xmpp.process() # 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.")

78
examples/user_location.py Executable file → Normal file
View File

@@ -1,9 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python
import sys import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
try: try:
import json import json
@@ -16,16 +16,16 @@ except ImportError:
print('This demo requires the requests package for using HTTP.') print('This demo requires the requests package for using HTTP.')
sys.exit() sys.exit()
from slixmpp import ClientXMPP from sleekxmpp import ClientXMPP
class LocationBot(ClientXMPP): class LocationBot(ClientXMPP):
def __init__(self, jid, password): def __init__(self, jid, password):
super().__init__(jid, password) super(LocationBot, self).__init__(jid, password)
self.add_event_handler('session_start', self.start) self.add_event_handler('session_start', self.start, threaded=True)
self.add_event_handler('user_location_publish', self.add_event_handler('user_location_publish',
self.user_location_publish) self.user_location_publish)
self.register_plugin('xep_0004') self.register_plugin('xep_0004')
@@ -38,9 +38,9 @@ class LocationBot(ClientXMPP):
self.current_tune = None self.current_tune = None
async def start(self, event): def start(self, event):
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
self['xep_0115'].update_caps() self['xep_0115'].update_caps()
print("Using freegeoip.net to get geolocation.") print("Using freegeoip.net to get geolocation.")
@@ -71,35 +71,55 @@ class LocationBot(ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
xmpp = LocationBot(args.jid, args.password) xmpp = LocationBot(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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() # 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.")

74
examples/user_tune.py Executable file → Normal file
View File

@@ -1,9 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python
import sys import sys
import logging import logging
from getpass import getpass import getpass
from argparse import ArgumentParser from optparse import OptionParser
try: try:
from appscript import * from appscript import *
@@ -11,13 +11,13 @@ except ImportError:
print('This demo requires the appscript package to interact with iTunes.') print('This demo requires the appscript package to interact with iTunes.')
sys.exit() sys.exit()
from slixmpp import ClientXMPP from sleekxmpp import ClientXMPP
class TuneBot(ClientXMPP): class TuneBot(ClientXMPP):
def __init__(self, jid, password): def __init__(self, jid, password):
super().__init__(jid, password) super(TuneBot, self).__init__(jid, password)
# Check for the current song every 5 seconds. # Check for the current song every 5 seconds.
self.schedule('Check Current Tune', 5, self._update_tune, repeat=True) self.schedule('Check Current Tune', 5, self._update_tune, repeat=True)
@@ -35,9 +35,9 @@ class TuneBot(ClientXMPP):
self.current_tune = None self.current_tune = None
async def start(self, event): def start(self, event):
self.send_presence() self.send_presence()
await self.get_roster() self.get_roster()
self['xep_0115'].update_caps() self['xep_0115'].update_caps()
def _update_tune(self): def _update_tune(self):
@@ -83,35 +83,55 @@ class TuneBot(ClientXMPP):
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.
parser = ArgumentParser() optp = OptionParser()
# Output verbosity options. # Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR", optp.add_option('-q', '--quiet', help='set logging to ERROR',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO) const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG", optp.add_option('-d', '--debug', help='set logging to DEBUG',
action="store_const", dest="loglevel", action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO) 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. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", optp.add_option("-j", "--jid", dest="jid",
help="JID to use") help="JID to use")
parser.add_argument("-p", "--password", dest="password", optp.add_option("-p", "--password", dest="password",
help="password to use") help="password to use")
args = parser.parse_args() opts, args = optp.parse_args()
# Setup logging. # Setup logging.
logging.basicConfig(level=args.loglevel, logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s') format='%(levelname)-8s %(message)s')
if args.jid is None: if opts.jid is None:
args.jid = input("Username: ") opts.jid = raw_input("Username: ")
if args.password is None: if opts.password is None:
args.password = getpass("Password: ") opts.password = getpass.getpass("Password: ")
xmpp = TuneBot(args.jid, args.password) xmpp = TuneBot(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. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() if xmpp.connect():
xmpp.process() # 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.")

233
ez_setup.py Normal file
View File

@@ -0,0 +1,233 @@
#!python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import sys
DEFAULT_VERSION = "0.6c7"
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = {
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
}
import sys, os
def _validate_md5(egg_name, data):
if egg_name in md5_data:
from md5 import md5
digest = md5(data).hexdigest()
if digest != md5_data[egg_name]:
print >>sys.stderr, (
"md5 validation of %s failed! (Possible download problem?)"
% egg_name
)
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, min_version=None,
download_delay=15
):
"""Automatically find/download setuptools and make it available on sys.path
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end with
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
it is not already available. If `download_delay` is specified, it should
be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
try:
import setuptools
if setuptools.__version__ == '0.0.1':
print >>sys.stderr, (
"You have an obsolete version of setuptools installed. Please\n"
"remove it from your system entirely before rerunning this script."
)
sys.exit(2)
except ImportError:
egg = download_setuptools(version, download_base, to_dir, download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
import pkg_resources
try:
if not min_version:
min_version = version
pkg_resources.require("setuptools>="+min_version)
except pkg_resources.VersionConflict, e:
# XXX could we install in a subprocess here?
print >>sys.stderr, (
"The required version of setuptools (>=%s) is not available, and\n"
"can't be installed while this script is running. Please install\n"
" a more recent version first.\n\n(Currently using %r)"
) % (min_version, e.args[0])
sys.exit(2)
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
from distutils import log
if delay:
log.warn("""
---------------------------------------------------------------------------
This script requires setuptools version %s to run (even to display
help). I will attempt to download it for you (from
%s), but
you may need to enable firewall access for this script first.
I will start the download in %d seconds.
(Note: if this machine does not have network access, please obtain the file
%s
and place it in this directory before rerunning this script.)
---------------------------------------------------------------------------""",
version, download_base, delay, url
); from time import sleep; sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
import setuptools
except ImportError:
egg = None
try:
egg = download_setuptools(version, delay=0)
sys.path.insert(0,egg)
from setuptools.command.easy_install import main
return main(list(argv)+[egg]) # we're done here
finally:
if egg and os.path.exists(egg):
os.unlink(egg)
else:
if setuptools.__version__ == '0.0.1':
# tell the user to uninstall obsolete version
use_setuptools(version)
req = "setuptools>="+version
import pkg_resources
try:
pkg_resources.require(req)
except pkg_resources.VersionConflict:
try:
from setuptools.command.easy_install import main
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print "Setuptools version",version,"or greater has been installed."
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames):
"""Update our built-in md5 registry"""
import re
from md5 import md5
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print >>sys.stderr, "Internal error!"
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])

View File

@@ -1,69 +0,0 @@
#!/usr/bin/env python3
import sys
import logging
import unittest
from argparse import ArgumentParser
from distutils.core import Command
from importlib import import_module
from pathlib import Path
def run_tests(filenames=None):
"""
Find and run all tests in the tests/ directory.
Excludes live tests (tests/live_*).
"""
if not filenames:
filenames = [i for i in Path('tests').glob('test_*')]
else:
filenames = [Path(i) for i in filenames]
modules = ['.'.join(test.parts[:-1] + (test.stem,)) for test in filenames]
suites = []
for filename in modules:
module = import_module(filename)
suites.append(module.suite)
tests = unittest.TestSuite(suites)
runner = unittest.TextTestRunner(verbosity=2)
# Disable logging output
logging.basicConfig(level=100)
logging.disable(100)
result = runner.run(tests)
return result
# Add a 'test' command for setup.py
class TestCommand(Command):
user_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
run_tests()
if __name__ == '__main__':
parser = ArgumentParser(description='Run unit tests.')
parser.add_argument('tests', metavar='TEST', nargs='*', help='list of tests to run, or nothing to run them all')
args = parser.parse_args()
result = run_tests(args.tests)
print("<tests %s ran='%s' errors='%s' fails='%s' success='%s'/>" % (
"xmlns='http//andyet.net/protocol/tests'",
result.testsRun, len(result.errors),
len(result.failures), result.wasSuccessful()))
sys.exit(not result.wasSuccessful())

191
setup.py
View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2007-2011 Nathanael C. Fritz # Copyright (C) 2007-2011 Nathanael C. Fritz
@@ -7,82 +7,131 @@
# This software is licensed as described in the README.rst and LICENSE # This software is licensed as described in the README.rst and LICENSE
# file, which you should have received as part of this distribution. # file, which you should have received as part of this distribution.
import os import sys
from pathlib import Path import codecs
from subprocess import call, DEVNULL, check_output, CalledProcessError
from tempfile import TemporaryFile
try: try:
from setuptools import setup from setuptools import setup, Command
except ImportError: except ImportError:
from distutils.core import setup from distutils.core import setup, Command
# from ez_setup import use_setuptools
from run_tests import TestCommand from testall import TestCommand
from slixmpp.version import __version__ from sleekxmpp.version import __version__
# if 'cygwin' in sys.platform.lower():
# min_version = '0.6c6'
# else:
# min_version = '0.6a9'
#
# try:
# use_setuptools(min_version=min_version)
# except TypeError:
# # locally installed ez_setup won't have min_version
# use_setuptools()
#
# from setuptools import setup, find_packages, Extension, Feature
VERSION = __version__ VERSION = __version__
DESCRIPTION = ('Slixmpp is an elegant Python library for XMPP (aka Jabber).') DESCRIPTION = 'SleekXMPP is an elegant Python library for XMPP (aka Jabber, Google Talk, etc).'
with open('README.rst', encoding='utf8') as readme: with codecs.open('README.rst', 'r', encoding='UTF-8') as readme:
LONG_DESCRIPTION = readme.read() LONG_DESCRIPTION = ''.join(readme)
CLASSIFIERS = [ CLASSIFIERS = [ 'Intended Audience :: Developers',
'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License',
'License :: OSI Approved :: MIT License', 'Programming Language :: Python',
'Programming Language :: Python', 'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.1',
'Topic :: Internet :: XMPP', 'Programming Language :: Python :: 3.2',
'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Libraries :: Python Modules',
] ]
packages = [str(mod.parent) for mod in Path('slixmpp').rglob('__init__.py')] packages = [ 'sleekxmpp',
'sleekxmpp/stanza',
def check_include(library_name, header): 'sleekxmpp/test',
command = [os.environ.get('PKG_CONFIG', 'pkg-config'), '--cflags', library_name] 'sleekxmpp/roster',
try: 'sleekxmpp/util',
cflags = check_output(command).decode('utf-8').split() 'sleekxmpp/util/sasl',
except FileNotFoundError: 'sleekxmpp/xmlstream',
print('pkg-config not found.') 'sleekxmpp/xmlstream/matcher',
return False 'sleekxmpp/xmlstream/handler',
except CalledProcessError: 'sleekxmpp/plugins',
# pkg-config already prints the missing libraries on stderr. 'sleekxmpp/plugins/xep_0004',
return False 'sleekxmpp/plugins/xep_0004/stanza',
command = [os.environ.get('CC', 'cc')] + cflags + ['-E', '-'] 'sleekxmpp/plugins/xep_0009',
with TemporaryFile('w+') as c_file: 'sleekxmpp/plugins/xep_0009/stanza',
c_file.write('#include <%s>' % header) 'sleekxmpp/plugins/xep_0012',
c_file.seek(0) 'sleekxmpp/plugins/xep_0013',
try: 'sleekxmpp/plugins/xep_0016',
return call(command, stdin=c_file, stdout=DEVNULL, stderr=DEVNULL) == 0 'sleekxmpp/plugins/xep_0027',
except FileNotFoundError: 'sleekxmpp/plugins/xep_0030',
print('%s headers not found.' % library_name) 'sleekxmpp/plugins/xep_0030/stanza',
return False 'sleekxmpp/plugins/xep_0033',
'sleekxmpp/plugins/xep_0047',
HAS_PYTHON_HEADERS = check_include('python3', 'Python.h') 'sleekxmpp/plugins/xep_0049',
HAS_STRINGPREP_HEADERS = check_include('libidn', 'stringprep.h') 'sleekxmpp/plugins/xep_0050',
'sleekxmpp/plugins/xep_0054',
ext_modules = None 'sleekxmpp/plugins/xep_0059',
if HAS_PYTHON_HEADERS and HAS_STRINGPREP_HEADERS: 'sleekxmpp/plugins/xep_0060',
try: 'sleekxmpp/plugins/xep_0060/stanza',
from Cython.Build import cythonize 'sleekxmpp/plugins/xep_0066',
except ImportError: 'sleekxmpp/plugins/xep_0077',
print('Cython not found, falling back to the slow stringprep module.') 'sleekxmpp/plugins/xep_0078',
else: 'sleekxmpp/plugins/xep_0080',
ext_modules = cythonize('slixmpp/stringprep.pyx') 'sleekxmpp/plugins/xep_0084',
else: 'sleekxmpp/plugins/xep_0085',
print('Falling back to the slow stringprep module.') 'sleekxmpp/plugins/xep_0086',
'sleekxmpp/plugins/xep_0091',
'sleekxmpp/plugins/xep_0092',
'sleekxmpp/plugins/xep_0107',
'sleekxmpp/plugins/xep_0108',
'sleekxmpp/plugins/xep_0115',
'sleekxmpp/plugins/xep_0118',
'sleekxmpp/plugins/xep_0128',
'sleekxmpp/plugins/xep_0131',
'sleekxmpp/plugins/xep_0153',
'sleekxmpp/plugins/xep_0172',
'sleekxmpp/plugins/xep_0184',
'sleekxmpp/plugins/xep_0186',
'sleekxmpp/plugins/xep_0191',
'sleekxmpp/plugins/xep_0198',
'sleekxmpp/plugins/xep_0199',
'sleekxmpp/plugins/xep_0202',
'sleekxmpp/plugins/xep_0203',
'sleekxmpp/plugins/xep_0221',
'sleekxmpp/plugins/xep_0224',
'sleekxmpp/plugins/xep_0231',
'sleekxmpp/plugins/xep_0235',
'sleekxmpp/plugins/xep_0249',
'sleekxmpp/plugins/xep_0257',
'sleekxmpp/plugins/xep_0258',
'sleekxmpp/plugins/xep_0279',
'sleekxmpp/plugins/xep_0280',
'sleekxmpp/plugins/xep_0297',
'sleekxmpp/plugins/xep_0308',
'sleekxmpp/plugins/xep_0313',
'sleekxmpp/features',
'sleekxmpp/features/feature_mechanisms',
'sleekxmpp/features/feature_mechanisms/stanza',
'sleekxmpp/features/feature_starttls',
'sleekxmpp/features/feature_bind',
'sleekxmpp/features/feature_session',
'sleekxmpp/features/feature_rosterver',
'sleekxmpp/features/feature_preapproval',
'sleekxmpp/thirdparty',
]
setup( setup(
name="slixmpp", name = "sleekxmpp",
version=VERSION, version = VERSION,
description=DESCRIPTION, description = DESCRIPTION,
long_description=LONG_DESCRIPTION, long_description = LONG_DESCRIPTION,
author='Florent Le Coz', author = 'Nathanael Fritz',
author_email='louiz@louiz.org', author_email = 'fritzy [at] netflint.net',
url='https://lab.louiz.org/poezio/slixmpp', url = 'http://github.com/fritzy/SleekXMPP',
license='MIT', license = 'MIT',
platforms=['any'], platforms = [ 'any' ],
packages=packages, packages = packages,
ext_modules=ext_modules, requires = [ 'dnspython', 'pyasn1', 'pyasn1_modules' ],
install_requires=['aiodns>=1.0', 'pyasn1', 'pyasn1_modules', 'aiohttp'], classifiers = CLASSIFIERS,
classifiers=CLASSIFIERS, cmdclass = {'test': TestCommand}
cmdclass={'test': TestCommand}
) )

19
sleekxmpp/__init__.py Normal file
View File

@@ -0,0 +1,19 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
from sleekxmpp.basexmpp import BaseXMPP
from sleekxmpp.clientxmpp import ClientXMPP
from sleekxmpp.componentxmpp import ComponentXMPP
from sleekxmpp.stanza import Message, Presence, Iq
from sleekxmpp.jid import JID, InvalidJID
from sleekxmpp.xmlstream.handler import *
from sleekxmpp.xmlstream import XMLStream, RestartStream
from sleekxmpp.xmlstream.matcher import *
from sleekxmpp.xmlstream.stanzabase import StanzaBase, ET
from sleekxmpp.version import __version__, __version_info__

View File

@@ -1,4 +1,4 @@
from slixmpp.xmlstream import JID from sleekxmpp.xmlstream import JID
class APIWrapper(object): class APIWrapper(object):

View File

@@ -1,38 +1,52 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
slixmpp.basexmpp sleekxmpp.basexmpp
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
This module provides the common XMPP functionality This module provides the common XMPP functionality
for both clients and components. for both clients and components.
Part of Slixmpp: The Slick XMPP Library Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz :copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details :license: MIT, see LICENSE for more details
""" """
import asyncio from __future__ import with_statement, unicode_literals
import sys
import logging import logging
import threading
from slixmpp import plugins, roster, stanza import sleekxmpp
from slixmpp.api import APIRegistry from sleekxmpp import plugins, features, roster
from slixmpp.exceptions import IqError, IqTimeout from sleekxmpp.api import APIRegistry
from sleekxmpp.exceptions import IqError, IqTimeout
from slixmpp.stanza import Message, Presence, Iq, StreamError from sleekxmpp.stanza import Message, Presence, Iq, StreamError
from slixmpp.stanza.roster import Roster from sleekxmpp.stanza.roster import Roster
from sleekxmpp.stanza.nick import Nick
from sleekxmpp.stanza.htmlim import HTMLIM
from slixmpp.xmlstream import XMLStream, JID from sleekxmpp.xmlstream import XMLStream, JID
from slixmpp.xmlstream import ET, register_stanza_plugin from sleekxmpp.xmlstream import ET, register_stanza_plugin
from slixmpp.xmlstream.matcher import MatchXPath from sleekxmpp.xmlstream.matcher import MatchXPath
from slixmpp.xmlstream.handler import Callback from sleekxmpp.xmlstream.handler import Callback
from slixmpp.xmlstream.stanzabase import XML_NS from sleekxmpp.xmlstream.stanzabase import XML_NS
from slixmpp.plugins import PluginManager, load_plugin from sleekxmpp.features import *
from sleekxmpp.plugins import PluginManager, register_plugin, load_plugin
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# In order to make sure that Unicode is handled properly
# in Python 2.x, reset the default encoding.
if sys.version_info < (3, 0):
from sleekxmpp.util.misc_ops import setdefaultencoding
setdefaultencoding('utf8')
class BaseXMPP(XMLStream): class BaseXMPP(XMLStream):
""" """
@@ -44,8 +58,8 @@ class BaseXMPP(XMLStream):
is used during initialization. is used during initialization.
""" """
def __init__(self, jid='', default_ns='jabber:client', **kwargs): def __init__(self, jid='', default_ns='jabber:client'):
XMLStream.__init__(self, **kwargs) XMLStream.__init__(self)
self.default_ns = default_ns self.default_ns = default_ns
self.stream_ns = 'http://etherx.jabber.org/streams' self.stream_ns = 'http://etherx.jabber.org/streams'
@@ -55,12 +69,12 @@ class BaseXMPP(XMLStream):
self.stream_id = None self.stream_id = None
#: The JabberID (JID) requested for this connection. #: The JabberID (JID) requested for this connection.
self.requested_jid = JID(jid) self.requested_jid = JID(jid, cache_lock=True)
#: The JabberID (JID) used by this connection, #: The JabberID (JID) used by this connection,
#: as set after session binding. This may even be a #: as set after session binding. This may even be a
#: different bare JID than what was requested. #: different bare JID than what was requested.
self.boundjid = JID(jid) self.boundjid = JID(jid, cache_lock=True)
self._expected_server_name = self.boundjid.host self._expected_server_name = self.boundjid.host
self._redirect_attempts = 0 self._redirect_attempts = 0
@@ -69,7 +83,7 @@ class BaseXMPP(XMLStream):
#: redirections that will be followed before quitting. #: redirections that will be followed before quitting.
self.max_redirects = 5 self.max_redirects = 5
self.session_bind_event = asyncio.Event() self.session_bind_event = threading.Event()
#: A dictionary mapping plugin names to plugins. #: A dictionary mapping plugin names to plugins.
self.plugin = PluginManager(self) self.plugin = PluginManager(self)
@@ -104,15 +118,12 @@ class BaseXMPP(XMLStream):
#: :attr:`use_message_ids` to `True` will assign all outgoing #: :attr:`use_message_ids` to `True` will assign all outgoing
#: messages an ID. Some plugin features require enabling #: messages an ID. Some plugin features require enabling
#: this option. #: this option.
self.use_message_ids = True self.use_message_ids = False
#: Presence updates may optionally be tagged with ID values. #: Presence updates may optionally be tagged with ID values.
#: Setting :attr:`use_message_ids` to `True` will assign all #: Setting :attr:`use_message_ids` to `True` will assign all
#: outgoing messages an ID. #: outgoing messages an ID.
self.use_presence_ids = True self.use_presence_ids = False
#: XEP-0359 <origin-id/> tag that gets added to <message/> stanzas.
self.use_origin_id = True
#: The API registry is a way to process callbacks based on #: The API registry is a way to process callbacks based on
#: JID+node combinations. Each callback in the registry is #: JID+node combinations. Each callback in the registry is
@@ -135,22 +146,15 @@ class BaseXMPP(XMLStream):
#: behave as expected when sending stanzas. #: behave as expected when sending stanzas.
self.sentpresence = False self.sentpresence = False
#: A reference to :mod:`slixmpp.stanza` to make accessing #: A reference to :mod:`sleekxmpp.stanza` to make accessing
#: stanza classes easier. #: stanza classes easier.
self.stanza = stanza self.stanza = sleekxmpp.stanza
self.register_handler( self.register_handler(
Callback('IM', Callback('IM',
MatchXPath('{%s}message/{%s}body' % (self.default_ns, MatchXPath('{%s}message/{%s}body' % (self.default_ns,
self.default_ns)), self.default_ns)),
self._handle_message)) self._handle_message))
self.register_handler(
Callback('IMError',
MatchXPath('{%s}message/{%s}error' % (self.default_ns,
self.default_ns)),
self._handle_message_error))
self.register_handler( self.register_handler(
Callback('Presence', Callback('Presence',
MatchXPath("{%s}presence" % self.default_ns), MatchXPath("{%s}presence" % self.default_ns),
@@ -196,6 +200,8 @@ class BaseXMPP(XMLStream):
# Initialize a few default stanza plugins. # Initialize a few default stanza plugins.
register_stanza_plugin(Iq, Roster) register_stanza_plugin(Iq, Roster)
register_stanza_plugin(Message, Nick)
register_stanza_plugin(Message, HTMLIM)
def start_stream_handler(self, xml): def start_stream_handler(self, xml):
"""Save the stream ID once the streams have been established. """Save the stream ID once the streams have been established.
@@ -210,18 +216,39 @@ class BaseXMPP(XMLStream):
log.warning('Legacy XMPP 0.9 protocol detected.') log.warning('Legacy XMPP 0.9 protocol detected.')
self.event('legacy_protocol') self.event('legacy_protocol')
def process(self, *, forever=True, timeout=None): def process(self, *args, **kwargs):
self.init_plugins() """Initialize plugins and begin processing the XML stream.
XMLStream.process(self, forever=forever, timeout=timeout)
def init_plugins(self): The number of threads used for processing stream events is determined
by :data:`HANDLER_THREADS`.
:param bool block: If ``False``, then event dispatcher will run
in a separate thread, allowing for the stream to be
used in the background for another application.
Otherwise, ``process(block=True)`` blocks the current
thread. Defaults to ``False``.
:param bool threaded: **DEPRECATED**
If ``True``, then event dispatcher will run
in a separate thread, allowing for the stream to be
used in the background for another application.
Defaults to ``True``. This does **not** mean that no
threads are used at all if ``threaded=False``.
Regardless of these threading options, these threads will
always exist:
- The event queue processor
- The send queue processor
- The scheduler
"""
for name in self.plugin: for name in self.plugin:
if not hasattr(self.plugin[name], 'post_inited'): if not hasattr(self.plugin[name], 'post_inited'):
if hasattr(self.plugin[name], 'post_init'): if hasattr(self.plugin[name], 'post_init'):
self.plugin[name].post_init() self.plugin[name].post_init()
self.plugin[name].post_inited = True self.plugin[name].post_inited = True
return XMLStream.process(self, *args, **kwargs)
def register_plugin(self, plugin, pconfig=None, module=None): def register_plugin(self, plugin, pconfig={}, module=None):
"""Register and configure a plugin for use in this stream. """Register and configure a plugin for use in this stream.
:param plugin: The name of the plugin class. Plugin names must :param plugin: The name of the plugin class. Plugin names must
@@ -292,11 +319,11 @@ class BaseXMPP(XMLStream):
:param id: An ideally unique ID value for this stanza thread. :param id: An ideally unique ID value for this stanza thread.
Defaults to 0. Defaults to 0.
:param ifrom: The from :class:`~slixmpp.xmlstream.jid.JID` :param ifrom: The from :class:`~sleekxmpp.xmlstream.jid.JID`
to use for this stanza. to use for this stanza.
:param ito: The destination :class:`~slixmpp.xmlstream.jid.JID` :param ito: The destination :class:`~sleekxmpp.xmlstream.jid.JID`
for this stanza. for this stanza.
:param itype: The :class:`~slixmpp.stanza.iq.Iq`'s type, :param itype: The :class:`~sleekxmpp.stanza.iq.Iq`'s type,
one of: ``'get'``, ``'set'``, ``'result'``, one of: ``'get'``, ``'set'``, ``'result'``,
or ``'error'``. or ``'error'``.
:param iquery: Optional namespace for adding a query element. :param iquery: Optional namespace for adding a query element.
@@ -310,14 +337,14 @@ class BaseXMPP(XMLStream):
return iq return iq
def make_iq_get(self, queryxmlns=None, ito=None, ifrom=None, iq=None): def make_iq_get(self, queryxmlns=None, ito=None, ifrom=None, iq=None):
"""Create an :class:`~slixmpp.stanza.iq.Iq` stanza of type ``'get'``. """Create an :class:`~sleekxmpp.stanza.iq.Iq` stanza of type ``'get'``.
Optionally, a query element may be added. Optionally, a query element may be added.
:param queryxmlns: The namespace of the query to use. :param queryxmlns: The namespace of the query to use.
:param ito: The destination :class:`~slixmpp.xmlstream.jid.JID` :param ito: The destination :class:`~sleekxmpp.xmlstream.jid.JID`
for this stanza. for this stanza.
:param ifrom: The ``'from'`` :class:`~slixmpp.xmlstream.jid.JID` :param ifrom: The ``'from'`` :class:`~sleekxmpp.xmlstream.jid.JID`
to use for this stanza. to use for this stanza.
:param iq: Optionally use an existing stanza instead :param iq: Optionally use an existing stanza instead
of generating a new one. of generating a new one.
@@ -334,13 +361,13 @@ class BaseXMPP(XMLStream):
def make_iq_result(self, id=None, ito=None, ifrom=None, iq=None): def make_iq_result(self, id=None, ito=None, ifrom=None, iq=None):
""" """
Create an :class:`~slixmpp.stanza.iq.Iq` stanza of type Create an :class:`~sleekxmpp.stanza.iq.Iq` stanza of type
``'result'`` with the given ID value. ``'result'`` with the given ID value.
:param id: An ideally unique ID value. May use :meth:`new_id()`. :param id: An ideally unique ID value. May use :meth:`new_id()`.
:param ito: The destination :class:`~slixmpp.xmlstream.jid.JID` :param ito: The destination :class:`~sleekxmpp.xmlstream.jid.JID`
for this stanza. for this stanza.
:param ifrom: The ``'from'`` :class:`~slixmpp.xmlstream.jid.JID` :param ifrom: The ``'from'`` :class:`~sleekxmpp.xmlstream.jid.JID`
to use for this stanza. to use for this stanza.
:param iq: Optionally use an existing stanza instead :param iq: Optionally use an existing stanza instead
of generating a new one. of generating a new one.
@@ -359,19 +386,19 @@ class BaseXMPP(XMLStream):
def make_iq_set(self, sub=None, ito=None, ifrom=None, iq=None): def make_iq_set(self, sub=None, ito=None, ifrom=None, iq=None):
""" """
Create an :class:`~slixmpp.stanza.iq.Iq` stanza of type ``'set'``. Create an :class:`~sleekxmpp.stanza.iq.Iq` stanza of type ``'set'``.
Optionally, a substanza may be given to use as the Optionally, a substanza may be given to use as the
stanza's payload. stanza's payload.
:param sub: Either an :param sub: Either an
:class:`~slixmpp.xmlstream.stanzabase.ElementBase` :class:`~sleekxmpp.xmlstream.stanzabase.ElementBase`
stanza object or an stanza object or an
:class:`~xml.etree.ElementTree.Element` XML object :class:`~xml.etree.ElementTree.Element` XML object
to use as the :class:`~slixmpp.stanza.iq.Iq`'s payload. to use as the :class:`~sleekxmpp.stanza.iq.Iq`'s payload.
:param ito: The destination :class:`~slixmpp.xmlstream.jid.JID` :param ito: The destination :class:`~sleekxmpp.xmlstream.jid.JID`
for this stanza. for this stanza.
:param ifrom: The ``'from'`` :class:`~slixmpp.xmlstream.jid.JID` :param ifrom: The ``'from'`` :class:`~sleekxmpp.xmlstream.jid.JID`
to use for this stanza. to use for this stanza.
:param iq: Optionally use an existing stanza instead :param iq: Optionally use an existing stanza instead
of generating a new one. of generating a new one.
@@ -391,7 +418,7 @@ class BaseXMPP(XMLStream):
condition='feature-not-implemented', condition='feature-not-implemented',
text=None, ito=None, ifrom=None, iq=None): text=None, ito=None, ifrom=None, iq=None):
""" """
Create an :class:`~slixmpp.stanza.iq.Iq` stanza of type ``'error'``. Create an :class:`~sleekxmpp.stanza.iq.Iq` stanza of type ``'error'``.
:param id: An ideally unique ID value. May use :meth:`new_id()`. :param id: An ideally unique ID value. May use :meth:`new_id()`.
:param type: The type of the error, such as ``'cancel'`` or :param type: The type of the error, such as ``'cancel'`` or
@@ -399,9 +426,9 @@ class BaseXMPP(XMLStream):
:param condition: The error condition. Defaults to :param condition: The error condition. Defaults to
``'feature-not-implemented'``. ``'feature-not-implemented'``.
:param text: A message describing the cause of the error. :param text: A message describing the cause of the error.
:param ito: The destination :class:`~slixmpp.xmlstream.jid.JID` :param ito: The destination :class:`~sleekxmpp.xmlstream.jid.JID`
for this stanza. for this stanza.
:param ifrom: The ``'from'`` :class:`~slixmpp.xmlstream.jid.JID` :param ifrom: The ``'from'`` :class:`~sleekxmpp.xmlstream.jid.JID`
to use for this stanza. to use for this stanza.
:param iq: Optionally use an existing stanza instead :param iq: Optionally use an existing stanza instead
of generating a new one. of generating a new one.
@@ -420,15 +447,15 @@ class BaseXMPP(XMLStream):
def make_iq_query(self, iq=None, xmlns='', ito=None, ifrom=None): def make_iq_query(self, iq=None, xmlns='', ito=None, ifrom=None):
""" """
Create or modify an :class:`~slixmpp.stanza.iq.Iq` stanza Create or modify an :class:`~sleekxmpp.stanza.iq.Iq` stanza
to use the given query namespace. to use the given query namespace.
:param iq: Optionally use an existing stanza instead :param iq: Optionally use an existing stanza instead
of generating a new one. of generating a new one.
:param xmlns: The query's namespace. :param xmlns: The query's namespace.
:param ito: The destination :class:`~slixmpp.xmlstream.jid.JID` :param ito: The destination :class:`~sleekxmpp.xmlstream.jid.JID`
for this stanza. for this stanza.
:param ifrom: The ``'from'`` :class:`~slixmpp.xmlstream.jid.JID` :param ifrom: The ``'from'`` :class:`~sleekxmpp.xmlstream.jid.JID`
to use for this stanza. to use for this stanza.
""" """
if not iq: if not iq:
@@ -454,7 +481,7 @@ class BaseXMPP(XMLStream):
mhtml=None, mfrom=None, mnick=None): mhtml=None, mfrom=None, mnick=None):
""" """
Create and initialize a new Create and initialize a new
:class:`~slixmpp.stanza.message.Message` stanza. :class:`~sleekxmpp.stanza.message.Message` stanza.
:param mto: The recipient of the message. :param mto: The recipient of the message.
:param mbody: The main contents of the message. :param mbody: The main contents of the message.
@@ -480,7 +507,7 @@ class BaseXMPP(XMLStream):
pto=None, ptype=None, pfrom=None, pnick=None): pto=None, ptype=None, pfrom=None, pnick=None):
""" """
Create and initialize a new Create and initialize a new
:class:`~slixmpp.stanza.presence.Presence` stanza. :class:`~sleekxmpp.stanza.presence.Presence` stanza.
:param pshow: The presence's show value. :param pshow: The presence's show value.
:param pstatus: The presence's status message. :param pstatus: The presence's status message.
@@ -504,7 +531,7 @@ class BaseXMPP(XMLStream):
mhtml=None, mfrom=None, mnick=None): mhtml=None, mfrom=None, mnick=None):
""" """
Create, initialize, and send a new Create, initialize, and send a new
:class:`~slixmpp.stanza.message.Message` stanza. :class:`~sleekxmpp.stanza.message.Message` stanza.
:param mto: The recipient of the message. :param mto: The recipient of the message.
:param mbody: The main contents of the message. :param mbody: The main contents of the message.
@@ -524,7 +551,7 @@ class BaseXMPP(XMLStream):
pto=None, pfrom=None, ptype=None, pnick=None): pto=None, pfrom=None, ptype=None, pnick=None):
""" """
Create, initialize, and send a new Create, initialize, and send a new
:class:`~slixmpp.stanza.presence.Presence` stanza. :class:`~sleekxmpp.stanza.presence.Presence` stanza.
:param pshow: The presence's show value. :param pshow: The presence's show value.
:param pstatus: The presence's status message. :param pstatus: The presence's status message.
@@ -541,7 +568,7 @@ class BaseXMPP(XMLStream):
ptype='subscribe', pnick=None): ptype='subscribe', pnick=None):
""" """
Create, initialize, and send a new Create, initialize, and send a new
:class:`~slixmpp.stanza.presence.Presence` stanza of :class:`~sleekxmpp.stanza.presence.Presence` stanza of
type ``'subscribe'``. type ``'subscribe'``.
:param pto: The recipient of a directed presence. :param pto: The recipient of a directed presence.
@@ -638,7 +665,7 @@ class BaseXMPP(XMLStream):
def set_jid(self, jid): def set_jid(self, jid):
"""Rip a JID apart and claim it as our own.""" """Rip a JID apart and claim it as our own."""
log.debug("setting jid to %s", jid) log.debug("setting jid to %s", jid)
self.boundjid = JID(jid) self.boundjid = JID(jid, cache_lock=True)
def getjidresource(self, fulljid): def getjidresource(self, fulljid):
if '/' in fulljid: if '/' in fulljid:
@@ -688,6 +715,7 @@ class BaseXMPP(XMLStream):
self.address = (host, port) self.address = (host, port)
self.default_domain = host self.default_domain = host
self.dns_records = None self.dns_records = None
self.reconnect_delay = None
self.reconnect() self.reconnect()
def _handle_message(self, msg): def _handle_message(self, msg):
@@ -696,12 +724,6 @@ class BaseXMPP(XMLStream):
msg['to'] = self.boundjid msg['to'] = self.boundjid
self.event('message', msg) self.event('message', msg)
def _handle_message_error(self, msg):
"""Process incoming message error stanzas."""
if not self.is_component and not msg['to'].bare:
msg['to'] = self.boundjid
self.event('message_error', msg)
def _handle_available(self, pres): def _handle_available(self, pres):
self.roster[pres['to']][pres['from']].handle_available(pres) self.roster[pres['to']][pres['from']].handle_available(pres)
@@ -753,9 +775,6 @@ class BaseXMPP(XMLStream):
Update the roster with presence information. Update the roster with presence information.
""" """
if self.roster[presence['from']].ignore_updates:
return
if not self.is_component and not presence['to'].bare: if not self.is_component and not presence['to'].bare:
presence['to'] = self.boundjid presence['to'] = self.boundjid
@@ -773,8 +792,8 @@ class BaseXMPP(XMLStream):
def exception(self, exception): def exception(self, exception):
"""Process any uncaught exceptions, notably """Process any uncaught exceptions, notably
:class:`~slixmpp.exceptions.IqError` and :class:`~sleekxmpp.exceptions.IqError` and
:class:`~slixmpp.exceptions.IqTimeout` exceptions. :class:`~sleekxmpp.exceptions.IqTimeout` exceptions.
:param exception: An unhandled :class:`Exception` object. :param exception: An unhandled :class:`Exception` object.
""" """
@@ -794,3 +813,23 @@ class BaseXMPP(XMLStream):
pass pass
else: else:
log.exception(exception) log.exception(exception)
# Restore the old, lowercased name for backwards compatibility.
basexmpp = BaseXMPP
# To comply with PEP8, method names now use underscores.
# Deprecated method names are re-mapped for backwards compatibility.
BaseXMPP.registerPlugin = BaseXMPP.register_plugin
BaseXMPP.makeIq = BaseXMPP.make_iq
BaseXMPP.makeIqGet = BaseXMPP.make_iq_get
BaseXMPP.makeIqResult = BaseXMPP.make_iq_result
BaseXMPP.makeIqSet = BaseXMPP.make_iq_set
BaseXMPP.makeIqError = BaseXMPP.make_iq_error
BaseXMPP.makeIqQuery = BaseXMPP.make_iq_query
BaseXMPP.makeQueryRoster = BaseXMPP.make_query_roster
BaseXMPP.makeMessage = BaseXMPP.make_message
BaseXMPP.makePresence = BaseXMPP.make_presence
BaseXMPP.sendMessage = BaseXMPP.send_message
BaseXMPP.sendPresence = BaseXMPP.send_presence
BaseXMPP.sendPresenceSubscription = BaseXMPP.send_presence_subscription

View File

@@ -1,27 +1,27 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
slixmpp.clientxmpp sleekxmpp.clientxmpp
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
This module provides XMPP functionality that This module provides XMPP functionality that
is specific to client connections. is specific to client connections.
Part of Slixmpp: The Slick XMPP Library Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz :copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details :license: MIT, see LICENSE for more details
""" """
import asyncio from __future__ import absolute_import, unicode_literals
import logging import logging
from slixmpp.jid import JID from sleekxmpp.stanza import StreamFeatures
from slixmpp.stanza import StreamFeatures from sleekxmpp.basexmpp import BaseXMPP
from slixmpp.basexmpp import BaseXMPP from sleekxmpp.exceptions import XMPPError
from slixmpp.exceptions import XMPPError from sleekxmpp.xmlstream import XMLStream
from slixmpp.xmlstream import XMLStream from sleekxmpp.xmlstream.matcher import StanzaPath, MatchXPath
from slixmpp.xmlstream.matcher import StanzaPath, MatchXPath from sleekxmpp.xmlstream.handler import Callback
from slixmpp.xmlstream.handler import Callback, CoroutineCallback
# Flag indicating if DNS SRV records are available for use. # Flag indicating if DNS SRV records are available for use.
try: try:
@@ -38,7 +38,7 @@ log = logging.getLogger(__name__)
class ClientXMPP(BaseXMPP): class ClientXMPP(BaseXMPP):
""" """
Slixmpp's client class. (Use only for good, not for evil.) SleekXMPP's client class. (Use only for good, not for evil.)
Typical use pattern: Typical use pattern:
@@ -52,22 +52,17 @@ class ClientXMPP(BaseXMPP):
:param jid: The JID of the XMPP user account. :param jid: The JID of the XMPP user account.
:param password: The password for the XMPP user account. :param password: The password for the XMPP user account.
:param ssl: **Deprecated.**
:param plugin_config: A dictionary of plugin configurations. :param plugin_config: A dictionary of plugin configurations.
:param plugin_whitelist: A list of approved plugins that :param plugin_whitelist: A list of approved plugins that
will be loaded when calling will be loaded when calling
:meth:`~slixmpp.basexmpp.BaseXMPP.register_plugins()`. :meth:`~sleekxmpp.basexmpp.BaseXMPP.register_plugins()`.
:param escape_quotes: **Deprecated.** :param escape_quotes: **Deprecated.**
""" """
def __init__(self, jid, password, plugin_config=None, def __init__(self, jid, password, plugin_config={}, plugin_whitelist=[],
plugin_whitelist=None, escape_quotes=True, sasl_mech=None, escape_quotes=True, sasl_mech=None, lang='en'):
lang='en', **kwargs): BaseXMPP.__init__(self, jid, 'jabber:client')
if not plugin_whitelist:
plugin_whitelist = []
if not plugin_config:
plugin_config = {}
BaseXMPP.__init__(self, jid, 'jabber:client', **kwargs)
self.escape_quotes = escape_quotes self.escape_quotes = escape_quotes
self.plugin_config = plugin_config self.plugin_config = plugin_config
@@ -101,29 +96,17 @@ class ClientXMPP(BaseXMPP):
self.add_event_handler('connected', self._reset_connection_state) self.add_event_handler('connected', self._reset_connection_state)
self.add_event_handler('session_bind', self._handle_session_bind) self.add_event_handler('session_bind', self._handle_session_bind)
self.add_event_handler('roster_update', self._handle_roster)
self.register_stanza(StreamFeatures) self.register_stanza(StreamFeatures)
self.register_handler( self.register_handler(
CoroutineCallback('Stream Features', Callback('Stream Features',
MatchXPath('{%s}features' % self.stream_ns), MatchXPath('{%s}features' % self.stream_ns),
self._handle_stream_features)) self._handle_stream_features))
def roster_push_filter(iq):
from_ = iq['from']
if from_ and from_ != JID('') and from_ != self.boundjid.bare:
reply = iq.reply()
reply['type'] = 'error'
reply['error']['type'] = 'cancel'
reply['error']['code'] = 503
reply['error']['condition'] = 'service-unavailable'
reply.send()
return
self.event('roster_update', iq)
self.register_handler( self.register_handler(
Callback('Roster Update', Callback('Roster Update',
StanzaPath('iq@type=set/roster'), StanzaPath('iq@type=set/roster'),
roster_push_filter)) self._handle_roster))
# Setup default stream features # Setup default stream features
self.register_plugin('feature_starttls') self.register_plugin('feature_starttls')
@@ -131,10 +114,8 @@ class ClientXMPP(BaseXMPP):
self.register_plugin('feature_session') self.register_plugin('feature_session')
self.register_plugin('feature_rosterver') self.register_plugin('feature_rosterver')
self.register_plugin('feature_preapproval') self.register_plugin('feature_preapproval')
self.register_plugin('feature_mechanisms') self.register_plugin('feature_mechanisms',
pconfig={'use_mech': sasl_mech} if sasl_mech else None)
if sasl_mech:
self['feature_mechanisms'].use_mech = sasl_mech
@property @property
def password(self): def password(self):
@@ -144,23 +125,23 @@ class ClientXMPP(BaseXMPP):
def password(self, value): def password(self, value):
self.credentials['password'] = value self.credentials['password'] = value
def connect(self, address=tuple(), use_ssl=False, def connect(self, address=tuple(), reattempt=True,
force_starttls=True, disable_starttls=False): use_tls=True, use_ssl=False):
"""Connect to the XMPP server. """Connect to the XMPP server.
When no address is given, a SRV lookup for the server will When no address is given, a SRV lookup for the server will
be attempted. If that fails, the server user in the JID be attempted. If that fails, the server user in the JID
will be used. will be used.
:param address: A tuple containing the server's host and port. :param address -- A tuple containing the server's host and port.
:param force_starttls: Indicates that negotiation should be aborted :param reattempt: If ``True``, repeat attempting to connect if an
if the server does not advertise support for error occurs. Defaults to ``True``.
STARTTLS. Defaults to ``True``. :param use_tls: Indicates if TLS should be used for the
:param disable_starttls: Disables TLS for the connection. connection. Defaults to ``True``.
Defaults to ``False``.
:param use_ssl: Indicates if the older SSL connection method :param use_ssl: Indicates if the older SSL connection method
should be used. Defaults to ``False``. should be used. Defaults to ``False``.
""" """
self.session_started_event.clear()
# If an address was provided, disable using DNS SRV lookup; # If an address was provided, disable using DNS SRV lookup;
# otherwise, use the domain from the client JID with the standard # otherwise, use the domain from the client JID with the standard
@@ -171,8 +152,11 @@ class ClientXMPP(BaseXMPP):
address = (self.boundjid.host, 5222) address = (self.boundjid.host, 5222)
self.dns_service = 'xmpp-client' self.dns_service = 'xmpp-client'
return XMLStream.connect(self, address[0], address[1], use_ssl=use_ssl, self._expected_server_name = self.boundjid.host
force_starttls=force_starttls, disable_starttls=disable_starttls)
return XMLStream.connect(self, address[0], address[1],
use_tls=use_tls, use_ssl=use_ssl,
reattempt=reattempt)
def register_feature(self, name, handler, restart=False, order=5000): def register_feature(self, name, handler, restart=False, order=5000):
"""Register a stream feature handler. """Register a stream feature handler.
@@ -205,10 +189,13 @@ class ClientXMPP(BaseXMPP):
``'none'``. If set to ``'remove'``, ``'none'``. If set to ``'remove'``,
the entry will be deleted. the entry will be deleted.
:param groups: The roster groups that contain this item. :param groups: The roster groups that contain this item.
:param block: Specify if the roster request will block
until a response is received, or a timeout
occurs. Defaults to ``True``.
:param timeout: The length of time (in seconds) to wait :param timeout: The length of time (in seconds) to wait
for a response before continuing if blocking for a response before continuing if blocking
is used. Defaults to is used. Defaults to
:attr:`~slixmpp.xmlstream.xmlstream.XMLStream.response_timeout`. :attr:`~sleekxmpp.xmlstream.xmlstream.XMLStream.response_timeout`.
:param callback: Optional reference to a stream handler function. :param callback: Optional reference to a stream handler function.
Will be executed when the roster is received. Will be executed when the roster is received.
Implies ``block=False``. Implies ``block=False``.
@@ -219,11 +206,12 @@ class ClientXMPP(BaseXMPP):
subscription = kwargs.get('subscription', current['subscription']) subscription = kwargs.get('subscription', current['subscription'])
groups = kwargs.get('groups', current['groups']) groups = kwargs.get('groups', current['groups'])
block = kwargs.get('block', True)
timeout = kwargs.get('timeout', None) timeout = kwargs.get('timeout', None)
callback = kwargs.get('callback', None) callback = kwargs.get('callback', None)
return self.client_roster.update(jid, name, subscription, groups, return self.client_roster.update(jid, name, subscription, groups,
timeout, callback) block, timeout, callback)
def del_roster_item(self, jid): def del_roster_item(self, jid):
"""Remove an item from the roster. """Remove an item from the roster.
@@ -234,11 +222,19 @@ class ClientXMPP(BaseXMPP):
""" """
return self.client_roster.remove(jid) return self.client_roster.remove(jid)
def get_roster(self, callback=None, timeout=None, timeout_callback=None): def get_roster(self, block=True, timeout=None, callback=None):
"""Request the roster from the server. """Request the roster from the server.
:param callback: Reference to a stream handler function. Will :param block: Specify if the roster request will block until a
response is received, or a timeout occurs.
Defaults to ``True``.
:param timeout: The length of time (in seconds) to wait for a response
before continuing if blocking is used.
Defaults to
:attr:`~sleekxmpp.xmlstream.xmlstream.XMLStream.response_timeout`.
:param callback: Optional reference to a stream handler function. Will
be executed when the roster is received. be executed when the roster is received.
Implies ``block=False``.
""" """
iq = self.Iq() iq = self.Iq()
iq['type'] = 'get' iq['type'] = 'get'
@@ -246,16 +242,15 @@ class ClientXMPP(BaseXMPP):
if 'rosterver' in self.features: if 'rosterver' in self.features:
iq['roster']['ver'] = self.client_roster.version iq['roster']['ver'] = self.client_roster.version
if callback is None: if not block and callback is None:
callback = lambda resp: self.event('roster_update', resp) callback = lambda resp: self._handle_roster(resp)
else:
orig_cb = callback
def wrapped(resp):
self.event('roster_update', resp)
orig_cb(resp)
callback = wrapped
return iq.send(callback, timeout, timeout_callback) response = iq.send(block, timeout, callback)
self.event('roster_received', response)
if block:
self._handle_roster(response)
return response
def _reset_connection_state(self, event=None): def _reset_connection_state(self, event=None):
#TODO: Use stream state here #TODO: Use stream state here
@@ -265,7 +260,7 @@ class ClientXMPP(BaseXMPP):
self.bindfail = False self.bindfail = False
self.features = set() self.features = set()
async def _handle_stream_features(self, features): def _handle_stream_features(self, features):
"""Process the received stream features. """Process the received stream features.
:param features: The features stanza. :param features: The features stanza.
@@ -273,11 +268,7 @@ class ClientXMPP(BaseXMPP):
for order, name in self._stream_feature_order: for order, name in self._stream_feature_order:
if name in features['features']: if name in features['features']:
handler, restart = self._stream_feature_handlers[name] handler, restart = self._stream_feature_handlers[name]
if asyncio.iscoroutinefunction(handler): if handler(features) and restart:
result = await handler(features)
else:
result = handler(features)
if result and restart:
# Don't continue if the feature requires # Don't continue if the feature requires
# restarting the XML stream. # restarting the XML stream.
return True return True
@@ -309,6 +300,7 @@ class ClientXMPP(BaseXMPP):
roster[jid].save(remove=(item['subscription'] == 'remove')) roster[jid].save(remove=(item['subscription'] == 'remove'))
self.event("roster_update", iq)
if iq['type'] == 'set': if iq['type'] == 'set':
resp = self.Iq(stype='result', resp = self.Iq(stype='result',
sto=iq['from'], sto=iq['from'],
@@ -319,7 +311,15 @@ class ClientXMPP(BaseXMPP):
def _handle_session_bind(self, jid): def _handle_session_bind(self, jid):
"""Set the client roster to the JID set by the server. """Set the client roster to the JID set by the server.
:param :class:`slixmpp.xmlstream.jid.JID` jid: The bound JID as :param :class:`sleekxmpp.xmlstream.jid.JID` jid: The bound JID as
dictated by the server. The same as :attr:`boundjid`. dictated by the server. The same as :attr:`boundjid`.
""" """
self.client_roster = self.roster[jid] self.client_roster = self.roster[jid]
# To comply with PEP8, method names now use underscores.
# Deprecated method names are re-mapped for backwards compatibility.
ClientXMPP.updateRoster = ClientXMPP.update_roster
ClientXMPP.delRosterItem = ClientXMPP.del_roster_item
ClientXMPP.getRoster = ClientXMPP.get_roster
ClientXMPP.registerFeature = ClientXMPP.register_feature

View File

@@ -1,25 +1,28 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
slixmpp.clientxmpp sleekxmpp.clientxmpp
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
This module provides XMPP functionality that This module provides XMPP functionality that
is specific to external server component connections. is specific to external server component connections.
Part of Slixmpp: The Slick XMPP Library Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz :copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details :license: MIT, see LICENSE for more details
""" """
from __future__ import absolute_import
import logging import logging
import sys
import hashlib import hashlib
from slixmpp.basexmpp import BaseXMPP from sleekxmpp.basexmpp import BaseXMPP
from slixmpp.xmlstream import XMLStream from sleekxmpp.xmlstream import XMLStream
from slixmpp.xmlstream import ET from sleekxmpp.xmlstream import ET
from slixmpp.xmlstream.matcher import MatchXPath from sleekxmpp.xmlstream.matcher import MatchXPath
from slixmpp.xmlstream.handler import Callback from sleekxmpp.xmlstream.handler import Callback
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -28,7 +31,7 @@ log = logging.getLogger(__name__)
class ComponentXMPP(BaseXMPP): class ComponentXMPP(BaseXMPP):
""" """
Slixmpp's basic XMPP server component. SleekXMPP's basic XMPP server component.
Use only for good, not for evil. Use only for good, not for evil.
@@ -39,20 +42,15 @@ class ComponentXMPP(BaseXMPP):
:param plugin_config: A dictionary of plugin configurations. :param plugin_config: A dictionary of plugin configurations.
:param plugin_whitelist: A list of approved plugins that :param plugin_whitelist: A list of approved plugins that
will be loaded when calling will be loaded when calling
:meth:`~slixmpp.basexmpp.BaseXMPP.register_plugins()`. :meth:`~sleekxmpp.basexmpp.BaseXMPP.register_plugins()`.
:param use_jc_ns: Indicates if the ``'jabber:client'`` namespace :param use_jc_ns: Indicates if the ``'jabber:client'`` namespace
should be used instead of the standard should be used instead of the standard
``'jabber:component:accept'`` namespace. ``'jabber:component:accept'`` namespace.
Defaults to ``False``. Defaults to ``False``.
""" """
def __init__(self, jid, secret, host=None, port=None, plugin_config=None, plugin_whitelist=None, use_jc_ns=False): def __init__(self, jid, secret, host=None, port=None,
plugin_config={}, plugin_whitelist=[], use_jc_ns=False):
if not plugin_whitelist:
plugin_whitelist = []
if not plugin_config:
plugin_config = {}
if use_jc_ns: if use_jc_ns:
default_ns = 'jabber:client' default_ns = 'jabber:client'
else: else:
@@ -60,7 +58,7 @@ class ComponentXMPP(BaseXMPP):
BaseXMPP.__init__(self, jid, default_ns) BaseXMPP.__init__(self, jid, default_ns)
self.auto_authorize = None self.auto_authorize = None
self.stream_header = '<stream:stream %s %s to="%s">' % ( self.stream_header = "<stream:stream %s %s to='%s'>" % (
'xmlns="jabber:component:accept"', 'xmlns="jabber:component:accept"',
'xmlns:stream="%s"' % self.stream_ns, 'xmlns:stream="%s"' % self.stream_ns,
jid) jid)
@@ -73,8 +71,6 @@ class ComponentXMPP(BaseXMPP):
self.plugin_whitelist = plugin_whitelist self.plugin_whitelist = plugin_whitelist
self.is_component = True self.is_component = True
self.sessionstarted = False
self.register_handler( self.register_handler(
Callback('Handshake', Callback('Handshake',
MatchXPath('{jabber:component:accept}handshake'), MatchXPath('{jabber:component:accept}handshake'),
@@ -82,9 +78,12 @@ class ComponentXMPP(BaseXMPP):
self.add_event_handler('presence_probe', self.add_event_handler('presence_probe',
self._handle_probe) self._handle_probe)
def connect(self, host=None, port=None, use_ssl=False): def connect(self, host=None, port=None, use_ssl=False,
use_tls=False, reattempt=True):
"""Connect to the server. """Connect to the server.
Setting ``reattempt`` to ``True`` will cause connection attempts to
be made every second until a successful connection is established.
:param host: The name of the desired server for the connection. :param host: The name of the desired server for the connection.
Defaults to :attr:`server_host`. Defaults to :attr:`server_host`.
@@ -92,6 +91,11 @@ class ComponentXMPP(BaseXMPP):
Defauts to :attr:`server_port`. Defauts to :attr:`server_port`.
:param use_ssl: Flag indicating if SSL should be used by connecting :param use_ssl: Flag indicating if SSL should be used by connecting
directly to a port using SSL. directly to a port using SSL.
:param use_tls: Flag indicating if TLS should be used, allowing for
connecting to a port without using SSL immediately and
later upgrading the connection.
:param reattempt: Flag indicating if the socket should reconnect
after disconnections.
""" """
if host is None: if host is None:
host = self.server_host host = self.server_host
@@ -100,9 +104,14 @@ class ComponentXMPP(BaseXMPP):
self.server_name = self.boundjid.host self.server_name = self.boundjid.host
if use_tls:
log.info("XEP-0114 components can not use TLS")
log.debug("Connecting to %s:%s", host, port) log.debug("Connecting to %s:%s", host, port)
return XMLStream.connect(self, host=host, port=port, return XMLStream.connect(self, host=host, port=port,
use_ssl=use_ssl) use_ssl=use_ssl,
use_tls=False,
reattempt=reattempt)
def incoming_filter(self, xml): def incoming_filter(self, xml):
""" """
@@ -114,6 +123,12 @@ class ComponentXMPP(BaseXMPP):
""" """
if xml.tag.startswith('{jabber:client}'): if xml.tag.startswith('{jabber:client}'):
xml.tag = xml.tag.replace('jabber:client', self.default_ns) xml.tag = xml.tag.replace('jabber:client', self.default_ns)
# The incoming_filter call is only made on top level stanza
# elements. So we manually continue filtering on sub-elements.
for sub in xml:
self.incoming_filter(sub)
return xml return xml
def start_stream_handler(self, xml): def start_stream_handler(self, xml):
@@ -127,11 +142,14 @@ class ComponentXMPP(BaseXMPP):
# Construct a hash of the stream ID and the component secret. # Construct a hash of the stream ID and the component secret.
sid = xml.get('id', '') sid = xml.get('id', '')
pre_hash = bytes('%s%s' % (sid, self.secret), 'utf-8') pre_hash = '%s%s' % (sid, self.secret)
if sys.version_info >= (3, 0):
# Handle Unicode byte encoding in Python 3.
pre_hash = bytes(pre_hash, 'utf-8')
handshake = ET.Element('{jabber:component:accept}handshake') handshake = ET.Element('{jabber:component:accept}handshake')
handshake.text = hashlib.sha1(pre_hash).hexdigest().lower() handshake.text = hashlib.sha1(pre_hash).hexdigest().lower()
self.send_xml(handshake) self.send_xml(handshake, now=True)
def _handle_handshake(self, xml): def _handle_handshake(self, xml):
"""The handshake has been accepted. """The handshake has been accepted.
@@ -139,9 +157,9 @@ class ComponentXMPP(BaseXMPP):
:param xml: The reply handshake stanza. :param xml: The reply handshake stanza.
""" """
self.session_bind_event.set() self.session_bind_event.set()
self.sessionstarted = True self.session_started_event.set()
self.event('session_bind', self.boundjid) self.event("session_bind", self.boundjid, direct=True)
self.event('session_start') self.event("session_start")
def _handle_probe(self, pres): def _handle_probe(self, pres):
self.roster[pres['to']][pres['from']].handle_probe(pres) self.roster[pres['to']][pres['from']].handle_probe(pres)

View File

@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
slixmpp.exceptions sleekxmpp.exceptions
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
Part of Slixmpp: The Slick XMPP Library Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz :copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details :license: MIT, see LICENSE for more details
@@ -17,11 +17,11 @@ class XMPPError(Exception):
to indicate that an error response stanza should be sent. to indicate that an error response stanza should be sent.
The exception method for stanza objects extending The exception method for stanza objects extending
:class:`~slixmpp.stanza.rootstanza.RootStanza` will create an error :class:`~sleekxmpp.stanza.rootstanza.RootStanza` will create an error
stanza and initialize any additional substanzas using the extension stanza and initialize any additional substanzas using the extension
information included in the exception. information included in the exception.
Meant for use in Slixmpp plugins and applications using Slixmpp. Meant for use in SleekXMPP plugins and applications using SleekXMPP.
Extension information can be included to add additional XML elements Extension information can be included to add additional XML elements
to the generated error stanza. to the generated error stanza.
@@ -42,7 +42,7 @@ class XMPPError(Exception):
Defaults to ``True``. Defaults to ``True``.
""" """
def __init__(self, condition='undefined-condition', text='', def __init__(self, condition='undefined-condition', text=None,
etype='cancel', extension=None, extension_ns=None, etype='cancel', extension=None, extension_ns=None,
extension_args=None, clear=True): extension_args=None, clear=True):
if extension_args is None: if extension_args is None:
@@ -56,18 +56,6 @@ class XMPPError(Exception):
self.extension_ns = extension_ns self.extension_ns = extension_ns
self.extension_args = extension_args self.extension_args = extension_args
def format(self):
"""
Format the error in a simple user-readable string.
"""
text = [self.etype, self.condition]
if self.text:
text.append(self.text)
if self.extension:
text.append(self.extension)
# TODO: handle self.extension_args
return ': '.join(text)
class IqTimeout(XMPPError): class IqTimeout(XMPPError):
@@ -77,11 +65,11 @@ class IqTimeout(XMPPError):
""" """
def __init__(self, iq): def __init__(self, iq):
super().__init__( super(IqTimeout, self).__init__(
condition='remote-server-timeout', condition='remote-server-timeout',
etype='cancel') etype='cancel')
#: The :class:`~slixmpp.stanza.iq.Iq` stanza whose response #: The :class:`~sleekxmpp.stanza.iq.Iq` stanza whose response
#: did not arrive before the timeout expired. #: did not arrive before the timeout expired.
self.iq = iq self.iq = iq
@@ -94,10 +82,10 @@ class IqError(XMPPError):
""" """
def __init__(self, iq): def __init__(self, iq):
super().__init__( super(IqError, self).__init__(
condition=iq['error']['condition'], condition=iq['error']['condition'],
text=iq['error']['text'], text=iq['error']['text'],
etype=iq['error']['type']) etype=iq['error']['type'])
#: The :class:`~slixmpp.stanza.iq.Iq` error result stanza. #: The :class:`~sleekxmpp.stanza.iq.Iq` error result stanza.
self.iq = iq self.iq = iq

View File

@@ -1,7 +1,7 @@
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Nathanael C. Fritz Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """

View File

@@ -0,0 +1,19 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
from sleekxmpp.plugins.base import register_plugin
from sleekxmpp.features.feature_bind.bind import FeatureBind
from sleekxmpp.features.feature_bind.stanza import Bind
register_plugin(FeatureBind)
# Retain some backwards compatibility
feature_bind = FeatureBind

View File

@@ -1,19 +1,18 @@
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Nathanael C. Fritz Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import asyncio
import logging import logging
from slixmpp.jid import JID from sleekxmpp.jid import JID
from slixmpp.stanza import Iq, StreamFeatures from sleekxmpp.stanza import Iq, StreamFeatures
from slixmpp.features.feature_bind import stanza from sleekxmpp.features.feature_bind import stanza
from slixmpp.xmlstream import register_stanza_plugin from sleekxmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins import BasePlugin from sleekxmpp.plugins import BasePlugin, register_plugin
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -35,34 +34,32 @@ class FeatureBind(BasePlugin):
register_stanza_plugin(Iq, stanza.Bind) register_stanza_plugin(Iq, stanza.Bind)
register_stanza_plugin(StreamFeatures, stanza.Bind) register_stanza_plugin(StreamFeatures, stanza.Bind)
async def _handle_bind_resource(self, features): def _handle_bind_resource(self, features):
""" """
Handle requesting a specific resource. Handle requesting a specific resource.
Arguments: Arguments:
features -- The stream features stanza. features -- The stream features stanza.
""" """
log.debug("Requesting resource: %s", self.xmpp.requested_jid.resource) log.debug("Requesting resource: %s", self.xmpp.boundjid.resource)
self.features = features
iq = self.xmpp.Iq() iq = self.xmpp.Iq()
iq['type'] = 'set' iq['type'] = 'set'
iq.enable('bind') iq.enable('bind')
if self.xmpp.requested_jid.resource: if self.xmpp.boundjid.resource:
iq['bind']['resource'] = self.xmpp.requested_jid.resource iq['bind']['resource'] = self.xmpp.boundjid.resource
response = iq.send(now=True)
await iq.send(callback=self._on_bind_response) self.xmpp.boundjid = JID(response['bind']['jid'], cache_lock=True)
def _on_bind_response(self, response):
self.xmpp.boundjid = JID(response['bind']['jid'])
self.xmpp.bound = True self.xmpp.bound = True
self.xmpp.event('session_bind', self.xmpp.boundjid) self.xmpp.event('session_bind', self.xmpp.boundjid, direct=True)
self.xmpp.session_bind_event.set() self.xmpp.session_bind_event.set()
self.xmpp.features.add('bind') self.xmpp.features.add('bind')
log.info("JID set to: %s", self.xmpp.boundjid.full) log.info("Node set to: %s", self.xmpp.boundjid.full)
if 'session' not in self.features['features']: if 'session' not in features['features']:
log.debug("Established Session") log.debug("Established Session")
self.xmpp.sessionstarted = True self.xmpp.sessionstarted = True
self.xmpp.event('session_start') self.xmpp.session_started_event.set()
self.xmpp.event("session_start")

View File

@@ -1,12 +1,12 @@
""" """
Slixmpp: The Slick XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Nathanael C. Fritz Copyright (C) 2011 Nathanael C. Fritz
This file is part of Slixmpp. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
from slixmpp.xmlstream import ElementBase from sleekxmpp.xmlstream import ElementBase
class Bind(ElementBase): class Bind(ElementBase):
@@ -16,6 +16,6 @@ class Bind(ElementBase):
name = 'bind' name = 'bind'
namespace = 'urn:ietf:params:xml:ns:xmpp-bind' namespace = 'urn:ietf:params:xml:ns:xmpp-bind'
interfaces = {'resource', 'jid'} interfaces = set(('resource', 'jid'))
sub_interfaces = interfaces sub_interfaces = interfaces
plugin_attrib = 'bind' plugin_attrib = 'bind'

Some files were not shown because too many files have changed in this diff Show More