summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLance stout <lancestout@gmail.com>2010-05-31 13:24:14 -0400
committerLance stout <lancestout@gmail.com>2010-05-31 13:24:14 -0400
commit629f6e76a9c003ef8befdfdf023f2e5b9eadc718 (patch)
tree6662bf0a70b2950d72bd2e3f788064f49557ba3c
parent1e3a6e1b5f06d307295be6b4ccaf75bf2a48399d (diff)
downloadslixmpp-629f6e76a9c003ef8befdfdf023f2e5b9eadc718.tar.gz
slixmpp-629f6e76a9c003ef8befdfdf023f2e5b9eadc718.tar.bz2
slixmpp-629f6e76a9c003ef8befdfdf023f2e5b9eadc718.tar.xz
slixmpp-629f6e76a9c003ef8befdfdf023f2e5b9eadc718.zip
Added implementation and tests for XEP-0085 - Chat State Notifications.
Chat states may be set using: msg['chat_state'].active() msg['chat_state'].composing() msg['chat_state'].gone() msg['chat_state'].inactive() msg['chat_state'].paused() Checking a chat state can be done with either: msg['chat_state'].getState() msg['chat_state'].name When a message with a chat state is receieved, the following events may occur: chatstate_active chatstate_composing chatstate_gone chatstate_inactive chatstate_paused where the event data is the message stanza. Note that currently these events are also triggered for messages sent by SleekXMPP, not just those received.
-rw-r--r--sleekxmpp/plugins/__init__.py2
-rw-r--r--sleekxmpp/plugins/xep_0085.py100
-rw-r--r--tests/test_chatstates.py47
3 files changed, 148 insertions, 1 deletions
diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py
index 1868365e..674c3de2 100644
--- a/sleekxmpp/plugins/__init__.py
+++ b/sleekxmpp/plugins/__init__.py
@@ -17,4 +17,4 @@
along with SleekXMPP; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
-__all__ = ['xep_0004', 'xep_0030', 'xep_0045', 'xep_0050', 'xep_0078', 'xep_0092', 'xep_0199', 'gmail_notify', 'xep_0060']
+__all__ = ['xep_0004', 'xep_0030', 'xep_0045', 'xep_0050', 'xep_0078', 'xep_0085', 'xep_0092', 'xep_0199', 'gmail_notify', 'xep_0060']
diff --git a/sleekxmpp/plugins/xep_0085.py b/sleekxmpp/plugins/xep_0085.py
new file mode 100644
index 00000000..e183ec27
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0085.py
@@ -0,0 +1,100 @@
+"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of SleekXMPP.
+
+ See the file license.txt for copying permissio
+"""
+
+import logging
+from . import base
+from .. xmlstream.handler.callback import Callback
+from .. xmlstream.matcher.xpath import MatchXPath
+from .. xmlstream.stanzabase import ElementBase, ET, JID
+from .. stanza.message import Message
+
+
+class ChatState(ElementBase):
+ namespace = 'http://jabber.org/protocol/chatstates'
+ plugin_attrib = 'chat_state'
+ interface = set(('state',))
+ states = set(('active', 'composing', 'gone', 'inactive', 'paused'))
+
+ def active(self):
+ self.setState('active')
+
+ def composing(self):
+ self.setState('composing')
+
+ def gone(self):
+ self.setState('gone')
+
+ def inactive(self):
+ self.setState('inactive')
+
+ def paused(self):
+ self.setState('paused')
+
+ def setState(self, state):
+ if state in self.states:
+ self.name = state
+ self.xml.tag = state
+ self.xml.attrib['xmlns'] = self.namespace
+
+ def getState(self):
+ return self.name
+
+# In order to match the various chat state elements,
+# we need one stanza object per state, even though
+# they are all the same except for the initial name
+# value. Do not depend on the type of the chat state
+# stanza object for the actual state.
+
+class Active(ChatState):
+ name = 'active'
+class Composing(ChatState):
+ name = 'composing'
+class Gone(ChatState):
+ name = 'gone'
+class Inactive(ChatState):
+ name = 'inactive'
+class Paused(ChatState):
+ name = 'paused'
+
+
+class xep_0085(base.base_plugin):
+ """
+ XEP-0085 Chat State Notifications
+ """
+
+ def plugin_init(self):
+ self.xep = '0085'
+ self.description = 'Chat State Notifications'
+
+ handlers = [('Active Chat State', 'active'),
+ ('Composing Chat State', 'composing'),
+ ('Gone Chat State', 'gone'),
+ ('Inactive Chat State', 'inactive'),
+ ('Paused Chat State', 'paused')]
+ for handler in handlers:
+ self.xmpp.registerHandler(
+ Callback(handler[0],
+ MatchXPath("{%s}message/{%s}%s" % (self.xmpp.default_ns,
+ ChatState.namespace,
+ handler[1])),
+ self._handleChatState))
+
+ self.xmpp.stanzaPlugin(Message, Active)
+ self.xmpp.stanzaPlugin(Message, Composing)
+ self.xmpp.stanzaPlugin(Message, Gone)
+ self.xmpp.stanzaPlugin(Message, Inactive)
+ self.xmpp.stanzaPlugin(Message, Paused)
+
+ def post_init(self):
+ base.base_plugin.post_init(self)
+ self.xmpp.plugin['xep_0030'].add_feature('http://jabber.org/protocol/chatstates')
+
+ def _handleChatState(self, msg):
+ state = msg['chat_state'].name
+ logging.debug("Chat State: %s, %s" % (state, msg['from'].jid))
+ self.xmpp.event('chatstate_%s' % state, msg)
diff --git a/tests/test_chatstates.py b/tests/test_chatstates.py
new file mode 100644
index 00000000..8878e318
--- /dev/null
+++ b/tests/test_chatstates.py
@@ -0,0 +1,47 @@
+import unittest
+from xml.etree import cElementTree as ET
+from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath
+from . import xmlcompare
+
+import sleekxmpp.plugins.xep_0085 as cs
+
+def stanzaPlugin(stanza, plugin):
+ stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin
+ stanza.plugin_tag_map["{%s}%s" % (plugin.namespace, plugin.name)] = plugin
+
+class testchatstates(unittest.TestCase):
+
+ def setUp(self):
+ self.cs = cs
+ stanzaPlugin(self.cs.Message, self.cs.Active)
+ stanzaPlugin(self.cs.Message, self.cs.Composing)
+ stanzaPlugin(self.cs.Message, self.cs.Gone)
+ stanzaPlugin(self.cs.Message, self.cs.Inactive)
+ stanzaPlugin(self.cs.Message, self.cs.Paused)
+
+ def try2Methods(self, xmlstring, msg):
+ msg2 = self.cs.Message(None, self.cs.ET.fromstring(xmlstring))
+ self.failUnless(xmlstring == str(msg) == str(msg2),
+ "Two methods for creating stanza don't match")
+
+ def testCreateChatState(self):
+ """Testing creating chat states."""
+ xmlstring = """<message><%s xmlns="http://jabber.org/protocol/chatstates" /></message>"""
+
+ msg = self.cs.Message()
+ msg['chat_state'].active()
+ self.try2Methods(xmlstring % 'active', msg)
+
+ msg['chat_state'].composing()
+ self.try2Methods(xmlstring % 'composing', msg)
+
+ msg['chat_state'].gone()
+ self.try2Methods(xmlstring % 'gone', msg)
+
+ msg['chat_state'].inactive()
+ self.try2Methods(xmlstring % 'inactive', msg)
+
+ msg['chat_state'].paused()
+ self.try2Methods(xmlstring % 'paused', msg)
+
+suite = unittest.TestLoader().loadTestsFromTestCase(testchatstates)