summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/.gitignore1
-rw-r--r--docs/_static/haiku.css35
-rw-r--r--docs/_static/images/from_&yet.pngbin0 -> 2812 bytes
-rw-r--r--docs/_static/pygments.css70
-rw-r--r--docs/_templates/layout.html70
-rw-r--r--docs/conf.py4
-rw-r--r--docs/getting_started/muc.rst206
-rw-r--r--docs/index.rst66
-rwxr-xr-xexamples/muc.py5
9 files changed, 450 insertions, 7 deletions
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 00000000..88f9974b
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1 @@
+_build/*
diff --git a/docs/_static/haiku.css b/docs/_static/haiku.css
index 615ed47b..3d8ee6a7 100644
--- a/docs/_static/haiku.css
+++ b/docs/_static/haiku.css
@@ -59,9 +59,10 @@ body {
margin: auto;
padding: 0px;
font-family: "Helvetica Neueu", Helvetica, sans-serif;
- min-width: 59em;
+ min-width: 30em;
max-width: 70em;
color: #444;
+ text-align: center;
}
div.footer {
@@ -124,21 +125,25 @@ a.headerlink:hover {
/* basic text elements */
div.content {
+ margin: auto;
margin-top: 20px;
- margin-left: 40px;
- margin-right: 40px;
margin-bottom: 50px;
font-size: 0.9em;
+ width: 700px;
+ text-align: left;
}
/* heading and navigation */
div.header {
position: relative;
+ margin: auto;
margin-top: 125px;
height: 85px;
padding: 0 40px;
font-family: "Yanone Kaffeesatz";
+ text-align: left;
+ width: 750px;
}
div.header h1 {
font-size: 2.6em;
@@ -185,12 +190,12 @@ div.topnav {
z-index: 0;
}
div.topnav p {
+ margin: auto;
margin-top: 0;
- margin-left: 40px;
- margin-right: 40px;
margin-bottom: 0px;
text-align: right;
font-size: 0.8em;
+ width: 750px;
}
div.bottomnav {
background: #eeeeee;
@@ -404,3 +409,23 @@ div.viewcode-block:target {
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;
+}
diff --git a/docs/_static/images/from_&yet.png b/docs/_static/images/from_&yet.png
new file mode 100644
index 00000000..ed5d8f33
--- /dev/null
+++ b/docs/_static/images/from_&yet.png
Binary files differ
diff --git a/docs/_static/pygments.css b/docs/_static/pygments.css
new file mode 100644
index 00000000..f04bc738
--- /dev/null
+++ b/docs/_static/pygments.css
@@ -0,0 +1,70 @@
+.highlight .hll { background-color: #ffffcc }
+.highlight { background: #000000; color: #f6f3e8; }
+.highlight .c { color: #7C7C7C; } /* Comment */
+.highlight .err { color: #f6f3e8; } /* Error */
+.highlight .g { color: #f6f3e8; } /* Generic */
+.highlight .k { color: #00ADEE; } /* Keyword */
+.highlight .l { color: #f6f3e8; } /* Literal */
+.highlight .n { color: #f6f3e8; } /* Name */
+.highlight .o { color: #f6f3e8; } /* Operator */
+.highlight .x { color: #f6f3e8; } /* Other */
+.highlight .p { color: #f6f3e8; } /* Punctuation */
+.highlight .cm { color: #7C7C7C; } /* Comment.Multiline */
+.highlight .cp { color: #96CBFE; } /* Comment.Preproc */
+.highlight .c1 { color: #7C7C7C; } /* Comment.Single */
+.highlight .cs { color: #7C7C7C; } /* Comment.Special */
+.highlight .gd { color: #f6f3e8; } /* Generic.Deleted */
+.highlight .ge { color: #f6f3e8; } /* Generic.Emph */
+.highlight .gr { color: #ffffff; background-color: #ff0000 } /* Generic.Error */
+.highlight .gh { color: #f6f3e8; font-weight: bold; } /* Generic.Heading */
+.highlight .gi { color: #f6f3e8; } /* Generic.Inserted */
+.highlight .go { color: #070707; } /* Generic.Output */
+.highlight .gp { color: #f6f3e8; } /* Generic.Prompt */
+.highlight .gs { color: #f6f3e8; } /* Generic.Strong */
+.highlight .gu { color: #f6f3e8; font-weight: bold; } /* Generic.Subheading */
+.highlight .gt { color: #ffffff; font-weight: bold; background-color: #FF6C60 } /* Generic.Traceback */
+.highlight .kc { color: #6699CC; } /* Keyword.Constant */
+.highlight .kd { color: #6699CC; } /* Keyword.Declaration */
+.highlight .kn { color: #6699CC; } /* Keyword.Namespace */
+.highlight .kp { color: #6699CC; } /* Keyword.Pseudo */
+.highlight .kr { color: #6699CC; } /* Keyword.Reserved */
+.highlight .kt { color: #FFFFB6; } /* Keyword.Type */
+.highlight .ld { color: #f6f3e8; } /* Literal.Date */
+.highlight .m { color: #FF73FD; } /* Literal.Number */
+.highlight .s { color: #F46DBA;/*#A8FF60;*/ } /* Literal.String */
+.highlight .na { color: #f6f3e8; } /* Name.Attribute */
+.highlight .nb { color: #f6f3e8; } /* Name.Builtin */
+.highlight .nc { color: #f6f3e8; } /* Name.Class */
+.highlight .no { color: #99CC99; } /* Name.Constant */
+.highlight .nd { color: #f6f3e8; } /* Name.Decorator */
+.highlight .ni { color: #E18964; } /* Name.Entity */
+.highlight .ne { color: #f6f3e8; } /* Name.Exception */
+.highlight .nf { color: #F64DBA; } /* Name.Function */
+.highlight .nl { color: #f6f3e8; } /* Name.Label */
+.highlight .nn { color: #f6f3e8; } /* Name.Namespace */
+.highlight .nx { color: #f6f3e8; } /* Name.Other */
+.highlight .py { color: #f6f3e8; } /* Name.Property */
+.highlight .nt { color: #00ADEE; } /* Name.Tag */
+.highlight .nv { color: #C6C5FE; } /* Name.Variable */
+.highlight .ow { color: #ffffff; } /* Operator.Word */
+.highlight .w { color: #f6f3e8; } /* Text.Whitespace */
+.highlight .mf { color: #FF73FD; } /* Literal.Number.Float */
+.highlight .mh { color: #FF73FD; } /* Literal.Number.Hex */
+.highlight .mi { color: #FF73FD; } /* Literal.Number.Integer */
+.highlight .mo { color: #FF73FD; } /* Literal.Number.Oct */
+.highlight .sb { color: #A8FF60; } /* Literal.String.Backtick */
+.highlight .sc { color: #A8FF60; } /* Literal.String.Char */
+.highlight .sd { color: #A8FF60; } /* Literal.String.Doc */
+.highlight .s2 { color: #A8FF60; } /* Literal.String.Double */
+.highlight .se { color: #A8FF60; } /* Literal.String.Escape */
+.highlight .sh { color: #A8FF60; } /* Literal.String.Heredoc */
+.highlight .si { color: #A8FF60; } /* Literal.String.Interpol */
+.highlight .sx { color: #A8FF60; } /* Literal.String.Other */
+.highlight .sr { color: #A8FF60; } /* Literal.String.Regex */
+.highlight .s1 { color: #A8FF60; } /* Literal.String.Single */
+.highlight .ss { color: #A8FF60; } /* Literal.String.Symbol */
+.highlight .bp { color: #f6f3e8; } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #C6C5FE; } /* Name.Variable.Class */
+.highlight .vg { color: #C6C5FE; } /* Name.Variable.Global */
+.highlight .vi { color: #C6C5FE; } /* Name.Variable.Instance */
+.highlight .il { color: #FF73FD; } /* Literal.Number.Integer.Long */
diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html
new file mode 100644
index 00000000..de6f7244
--- /dev/null
+++ b/docs/_templates/layout.html
@@ -0,0 +1,70 @@
+{#
+ haiku/layout.html
+ ~~~~~~~~~~~~~~~~~
+
+ Sphinx layout template for the haiku theme.
+
+ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+#}
+{% extends "basic/layout.html" %}
+{% set script_files = script_files + ['_static/theme_extras.js'] %}
+{% set css_files = css_files + ['_static/print.css'] %}
+
+{# do not display relbars #}
+{% block relbar1 %}{% endblock %}
+{% block relbar2 %}{% endblock %}
+
+{% macro nav() %}
+ <p>
+ {%- block haikurel1 %}
+ {%- endblock %}
+ {%- if prev %}
+ «&#160;&#160;<a href="{{ prev.link|e }}">{{ prev.title }}</a>
+ &#160;&#160;::&#160;&#160;
+ {%- endif %}
+ <a class="uplink" href="{{ pathto(master_doc) }}">{{ _('Contents') }}</a>
+ {%- if next %}
+ &#160;&#160;::&#160;&#160;
+ <a href="{{ next.link|e }}">{{ next.title }}</a>&#160;&#160;»
+ {%- endif %}
+ {%- block haikurel2 %}
+ {%- endblock %}
+ </p>
+{% endmacro %}
+
+{% block content %}
+ <div class="header">
+ {%- block haikuheader %}
+ {%- if theme_full_logo != "false" %}
+ <a href="{{ pathto('index') }}">
+ <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
+ </a>
+ {%- else %}
+ {%- if logo -%}
+ <img class="rightlogo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
+ {%- endif -%}
+ <h1 class="heading"><a href="{{ pathto('index') }}">
+ <span>{{ title|striptags }}</span></a></h1>
+ <h2 class="heading"><span>{{ shorttitle|e }}</span></h2>
+ {%- endif %}
+ {%- endblock %}
+ </div>
+ <div class="topnav">
+ {{ nav() }}
+ </div>
+ <div class="content">
+ {#{%- if display_toc %}
+ <div id="toc">
+ <h3>Table Of Contents</h3>
+ {{ toc }}
+ </div>
+ {%- endif %}#}
+ {% block body %}{% endblock %}
+ </div>
+ <div class="bottomnav">
+ {{ nav() }}
+ </div>
+ <a id="from_andyet" href="http://andyet.net"><h2>From &amp;yet</h2></a>
+{% endblock %}
+
diff --git a/docs/conf.py b/docs/conf.py
index dd83f243..72e39d0f 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -50,7 +50,7 @@ copyright = u'2011, Nathan Fritz, Lance Stout'
# The short X.Y version.
version = '1.0'
# The full version, including alpha/beta/rc tags.
-release = '1.0RC3'
+release = '1.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -91,7 +91,7 @@ pygments_style = 'tango'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'nature'
+html_theme = 'haiku'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
diff --git a/docs/getting_started/muc.rst b/docs/getting_started/muc.rst
index 08f721f7..26e1fa57 100644
--- a/docs/getting_started/muc.rst
+++ b/docs/getting_started/muc.rst
@@ -1,2 +1,208 @@
+.. _mucbot:
+
+=========================
Mulit-User Chat (MUC) Bot
=========================
+
+.. note::
+
+ If you have any issues working through this quickstart guide
+ or the other tutorials here, please either send a message to the
+ `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
+
+
+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
+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
+and "lurk" indefinitely, responding with a generic message to anyone
+that mentions its nickname. It will also greet members as they join the
+chat room.
+
+.. _`multi-user chat`: http://xmpp.org/extensions/xep-0045.html
+
+Joining The Room
+----------------
+
+As usual, our code will be based on the pattern explained in :ref:`echobot`.
+To start, we create an ``MUCBot`` class based on
+:class:`ClientXMPP <sleekxmpp.clientxmpp.ClientXMPP>` and which accepts
+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
+:term:`event handler` for the :term:`session_start` event.
+
+
+.. code-block:: python
+
+ import sleekxmpp
+
+ class MUCBot(sleekxmpp.ClientXMPP):
+
+ def __init__(self, jid, password, room, nick):
+ sleekxmpp.ClientXMPP.__init__(self, jid, password)
+
+ self.room = room
+ self.nick = nick
+
+ self.add_event_handler("session_start", self.start)
+
+After initialization, we also need to register the MUC (XEP-0045) plugin
+so that we can make use of the group chat plugin's methods and events.
+
+.. code-block:: python
+
+ xmpp.register_plugin('xep_0045')
+
+Finally, we can make our bot join the chat room once an XMPP session
+has been established:
+
+.. code-block:: python
+
+ def start(self, event):
+ self.get_roster()
+ self.send_presence()
+ self.plugin['xep_0045'].joinMUC(self.room,
+ self.nick,
+ wait=True)
+
+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
+``joinMUC`` method of the MUC plugin.
+
+.. note::
+
+ The :attr:`plugin <sleekxmpp.basexmpp.BaseXMPP.plugin>` attribute is
+ dictionary that maps to instances of plugins that we have previously
+ registered, by their names.
+
+
+Adding Functionality
+--------------------
+
+Currently, our bot just sits dormantly inside the chat room, but we
+would like it to respond to two distinct events by issuing a generic
+message in each case to the chat room. In particular, when a member
+mentions the bot's nickname inside the chat room, and when a member
+joins the chat room.
+
+Responding to Mentions
+~~~~~~~~~~~~~~~~~~~~~~
+
+Whenever a user mentions our bot's nickname in chat, our bot will
+respond with a generic message resembling *"I heard that, user."* We do
+this by examining all of the messages sent inside the chat and looking
+for the ones which contain the nickname string.
+
+First, we register an event handler for the :term:`groupchat_message`
+event inside the bot's ``__init__`` function.
+
+.. note::
+
+ We do not register a handler for the :term:`message` event in this
+ bot, but if we did, the group chat message would have been sent to
+ both handlers.
+
+.. code-block:: python
+
+ def __init__(self, jid, password, room, nick):
+ sleekxmpp.ClientXMPP.__init__(self, jid, password)
+
+ self.room = room
+ self.nick = nick
+
+ self.add_event_handler("session_start", self.start)
+ self.add_event_handler("groupchat_message", self.muc_message)
+
+Then, we can send our generic message whenever the bot's nickname gets
+mentioned.
+
+.. warning::
+
+ Always check that a message is not from yourself,
+ otherwise you will create an infinite loop responding
+ to your own messages.
+
+.. code-block:: python
+
+ def muc_message(self, msg):
+ if msg['mucnick'] != self.nick and self.nick in msg['body']:
+ self.send_message(mto=msg['from'].bare,
+ mbody="I heard that, %s." % msg['mucnick'],
+ mtype='groupchat')
+
+
+Greeting Members
+~~~~~~~~~~~~~~~~
+
+Now we want to greet member whenever they join the group chat. To
+do this we will use the dynamic ``muc::room@server::got_online`` [1]_
+event so it's a good idea to register an event handler for it.
+
+.. note::
+
+ The groupchat_presence event is triggered whenever a
+ presence stanza is received from any chat room, including
+ any presences you send yourself. To limit event handling
+ to a single room, use the events ``muc::room@server::presence``,
+ ``muc::room@server::got_online``, or ``muc::room@server::got_offline``.
+
+.. code-block:: python
+
+ def __init__(self, jid, password, room, nick):
+ sleekxmpp.ClientXMPP.__init__(self, jid, password)
+
+ self.room = room
+ self.nick = nick
+
+ self.add_event_handler("session_start", self.start)
+ self.add_event_handler("groupchat_message", self.muc_message)
+ self.add_event_handler("muc::%s::got_online" % self.room,
+ self.muc_online)
+
+Now all that's left to do is to greet them:
+
+.. code-block:: python
+
+ def muc_online(self, presence):
+ if presence['muc']['nick'] != self.nick:
+ self.send_message(mto=presence['from'].bare,
+ mbody="Hello, %s %s" % (presence['muc']['role'],
+ presence['muc']['nick']),
+ mtype='groupchat')
+
+.. [1] this is similar to the :term:`got_online` event and is sent by
+ the xep_0045 plugin whenever a member joins the referenced
+ MUC chat room.
+
+
+Final Product
+-------------
+
+.. compound::
+
+ The final step is to create a small runner script for initialising our ``MUCBot`` class and adding some
+ basic configuration options. By following the basic boilerplate pattern in :ref:`echobot`, we arrive
+ at the code below. To experiment with this example, you can use:
+
+ .. code-block:: sh
+
+ python muc.py -d -j jid@example.com -r room@muc.example.net -n lurkbot
+
+ which will prompt for the password, log in, and join the group chat. To test, open
+ your regular IM client and join the same group chat that you sent the bot to. You
+ will see ``lurkbot`` as one of the members in the group chat, and that it greeted
+ you upon entry. Send a message with the string "lurkbot" inside the body text, and you
+ will also see that it responds with our pre-programmed customized message.
+
+.. include:: ../../examples/muc.py
+ :literal:
diff --git a/docs/index.rst b/docs/index.rst
index fc6541d6..fe7df7f9 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -59,6 +59,72 @@ SleekXMPP's design goals and philosphy are:
sensible defaults and appropriate abstractions. XML can be ugly to work
with, but it doesn't have to be that way.
+Here's your first SleekXMPP Bot:
+--------------------------------
+
+.. code-block:: python
+
+ import logging
+
+ from sleekxmpp import ClientXMPP
+ from sleekxmpp.exceptions import IqError, IqTimeout
+
+
+ class EchoBot(ClientXMPP):
+
+ def __init__(self, jid, password):
+ ClientXMPP.__init__(self, jid, password)
+
+ self.add_event_handler("session_start", self.session_start)
+ self.add_event_handler("message", self.message)
+
+ self.register_plugin('xep_0030') # Service Discovery
+ self.register_plugin('xep_0199') # XMPP Ping
+
+ # Here's how to access plugins once you've registered them:
+ # self['xep_0030'].add_feature('echodemo')
+ # You can also use self.plugin['xep_0030']
+
+ # 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):
+ self.send_presence()
+ self.get_roster()
+
+ # Most get_*/set_* methods from plugins use Iq stanzas, which
+ # can generate IqError and IqTimeout exceptions
+ #
+ # 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):
+ if msg['type'] in ('chat', 'normal'):
+ msg.reply("Thanks for sending\n%(body)s" % msg).send()
+
+
+ if __name__ == '__main__':
+ # Ideally use optparse or argparse to get JID,
+ # password, and log level.
+
+ logging.basicConfig(level=logging.DEBUG,
+ format='%(levelname)-8s %(message)s')
+
+ xmpp = EchoBot('somejid@example.com', 'use_getpass')
+ xmpp.connect()
+ xmpp.process(block=True)
+
+
+
Getting Started (with Examples)
-------------------------------
.. toctree::
diff --git a/examples/muc.py b/examples/muc.py
index 8fe2eb49..cfb99c23 100755
--- a/examples/muc.py
+++ b/examples/muc.py
@@ -76,8 +76,13 @@ class MUCBot(sleekxmpp.ClientXMPP):
event does not provide any additional
data.
"""
+<<<<<<< HEAD
self.getRoster()
self.sendPresence()
+=======
+ self.get_roster()
+ self.send_presence()
+>>>>>>> docs
self.plugin['xep_0045'].joinMUC(self.room,
self.nick,
# If a room password is needed, use: