# HG changeset patch # User souliane # Date 1381144197 -7200 # Node ID b6c22d9f593ae4e492b94c6b4a304324fa92f2ef # Parent e26134122ed7226a395501872637d8b5afb4d3a5 plugin xep-0085: bug fix + improvement diff -r e26134122ed7 -r b6c22d9f593a src/plugins/plugin_xep_0085.py --- a/src/plugins/plugin_xep_0085.py Mon Oct 07 14:58:41 2013 +0200 +++ b/src/plugins/plugin_xep_0085.py Mon Oct 07 13:09:57 2013 +0200 @@ -18,7 +18,7 @@ # along with this program. If not, see . from sat.core import exceptions -from logging import debug, info, error +from logging import info from wokkel import disco, iwokkel from zope.interface import implements from twisted.words.protocols.jabber.jid import JID @@ -89,7 +89,7 @@ } def __init__(self, host): - info(_("CSN plugin initialization")) + info(_("Chat State Notifications plugin initialization")) self.host = host # parameter value is retrieved before each use @@ -99,7 +99,7 @@ host.trigger.add("MessageReceived", self.messageReceivedTrigger) host.trigger.add("sendMessageXml", self.sendMessageXmlTrigger) host.trigger.add("paramUpdateTrigger", self.paramUpdateTrigger) - # TODO: handle profile disconnexion (free memory in entity data) + # TODO: handle profile disconnection (free memory in entity data) # args: to_s (jid as string), profile host.bridge.addMethod("chatStateComposing", ".plugin", in_sign='ss', @@ -113,9 +113,11 @@ def updateEntityData(self, entity_jid, value, profile): """ - Update the entity data and reset the chat state display - if the notification has been disabled. Parameter "entity_jid" - could be @ALL@ to update all entities. + Update the entity data of the given profile for one or all contacts. + Reset the chat state(s) display if the notification has been disabled. + @param entity_jid: contact's JID, or '@ALL@' to update all contacts. + @param value: True, False or '@NONE@' to delete the entity data + @param profile: current profile """ self.host.memory.updateEntityData(entity_jid, ENTITY_KEY, value, profile) if not value or value == "@NONE@": @@ -124,9 +126,10 @@ def paramUpdateTrigger(self, name, value, category, type, profile): """ - Reset all the existing chat state entity data associated - with this profile after a parameter modification (value - different then "true" would delete the entity data). + Reset all the existing chat state entity data associated with this profile after a parameter modification. + @param name: parameter name + @param value: "true" to activate the notifications, or any other value to delete it + @param category: parameter category """ if (category, name) == (PARAM_KEY, PARAM_NAME): self.updateEntityData("@ALL@", True if value == "true" else "@NONE@", profile) @@ -151,6 +154,7 @@ except StopIteration: # contact didn't enable Chat State Notifications self.updateEntityData(from_jid, False, profile) + return True except StopIteration: pass @@ -160,8 +164,7 @@ and child.defaultUri == NS_CHAT_STATES] for state in state_list: # there must be only one state according to the XEP - self.host.bridge.chatStateReceived(message.getAttribute("from"), - state, profile) + self.host.bridge.chatStateReceived(message.getAttribute("from"), state, profile) break return True @@ -170,19 +173,8 @@ Eventually add the chat state to the message and initiate the state machine when sending an "active" state. """ - if not self.host.memory.getParamA(PARAM_NAME, PARAM_KEY, profile_key=profile): - return True - - # check if notifications should be sent to this contact - contact_enabled = True to_jid = JID(message.getAttribute("to")) - try: - contact_enabled = self.host.memory.getEntityData( - to_jid, [ENTITY_KEY], profile)[ENTITY_KEY] - except (exceptions.UnknownEntityError, KeyError): - # enable it for the first time - self.updateEntityData(to_jid, True, profile) - if not contact_enabled: + if not self.__checkActivation(to_jid, forceEntityData=True, profile=profile): return True try: # message with a body always mean active state @@ -197,6 +189,28 @@ message.addElement(state, NS_CHAT_STATES) return True + def __checkActivation(self, to_jid, forceEntityData, profile): + """ + @param to_joid: the contact's JID + @param forceEntityData: if set to True, a non-existing + entity data will be considered to be True (and initialized) + @param: current profile + @return: True if the notifications should be sent to this JID. + """ + # check if the parameter is active + if not self.host.memory.getParamA(PARAM_NAME, PARAM_KEY, profile_key=profile): + return False + # check if notifications should be sent to this contact + try: + return self.host.memory.getEntityData(to_jid, [ENTITY_KEY], profile)[ENTITY_KEY] + except (exceptions.UnknownEntityError, KeyError): + if forceEntityData: + # enable it for the first time + self.updateEntityData(to_jid, True, profile) + return True + # wait for the first message before sending states + return False + def __chatStateInit(self, to_jid, mess_type, profile): """ Data initialization for the chat state machine. @@ -222,6 +236,7 @@ profile = self.host.memory.getProfileName(profile_key) if profile is None: raise exceptions.ProfileUnknownError + return self.__chatStateInit(to_jid, mess_type, profile) self.map[profile][to_jid]._onEvent("active") @@ -233,26 +248,20 @@ data associated to the target JID. TODO: try to optimize this method which is called often """ - # check if the parameter is active - if not self.host.memory.getParamA(PARAM_NAME, PARAM_KEY, profile_key=profile_key): - return # TODO: use also the JID resource in the map key to_jid = JID(to_jid_s).userhostJID() profile = self.host.memory.getProfileName(profile_key) if profile is None: raise exceptions.ProfileUnknownError - # check if notifications should be sent to this contact - contact_enabled = True + return + if not self.__checkActivation(to_jid, forceEntityData=False, profile=profile): + return try: - contact_enabled = self.host.memory.getEntityData( - to_jid, [ENTITY_KEY], profile)[ENTITY_KEY] - except (exceptions.UnknownEntityError, KeyError): - # wait for the first message before sending states + self.map[profile][to_jid]._onEvent("composing") + except AttributeError: + # no message has been sent/received since the notifications + # have been enabled, it's better to wait for a first one pass - if not contact_enabled: - return True - # now we are sure that the state should be sent - self.map[profile][to_jid]._onEvent("composing") class ChatStateMachine: