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.
This commit is contained in:
Lance stout 2010-05-31 13:24:14 -04:00
parent 1e3a6e1b5f
commit 629f6e76a9
3 changed files with 148 additions and 1 deletions

View file

@ -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']

View file

@ -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)

47
tests/test_chatstates.py Normal file
View file

@ -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)