comparison src/plugins/plugin_xep_0085.py @ 1252:5ff9f9af9d1f

plugin XEP-0085: use the full JID + fixes bad entity data "type" value
author souliane <souliane@mailoo.org>
date Mon, 20 Oct 2014 12:57:55 +0200
parents 301b342c697a
children c13a46207410
comparison
equal deleted inserted replaced
1251:51a85e8f599a 1252:5ff9f9af9d1f
125 self.host.memory.updateEntityData(entity_jid, ENTITY_KEY, value, profile) 125 self.host.memory.updateEntityData(entity_jid, ENTITY_KEY, value, profile)
126 if not value or value == C.PROF_KEY_NONE: 126 if not value or value == C.PROF_KEY_NONE:
127 # disable chat state for this or these contact(s) 127 # disable chat state for this or these contact(s)
128 self.host.bridge.chatStateReceived(unicode(entity_jid), "", profile) 128 self.host.bridge.chatStateReceived(unicode(entity_jid), "", profile)
129 129
130 def paramUpdateTrigger(self, name, value, category, type, profile): 130 def paramUpdateTrigger(self, name, value, category, type_, profile):
131 """ 131 """
132 Reset all the existing chat state entity data associated with this profile after a parameter modification. 132 Reset all the existing chat state entity data associated with this profile after a parameter modification.
133 @param name: parameter name 133 @param name: parameter name
134 @param value: "true" to activate the notifications, or any other value to delete it 134 @param value: "true" to activate the notifications, or any other value to delete it
135 @param category: parameter category 135 @param category: parameter category
136 @param type_: parameter type
136 """ 137 """
137 if (category, name) == (PARAM_KEY, PARAM_NAME): 138 if (category, name) == (PARAM_KEY, PARAM_NAME):
138 self.updateEntityData("@ALL@", True if value == "true" else C.PROF_KEY_NONE, profile) 139 self.updateEntityData("@ALL@", True if value == "true" else C.PROF_KEY_NONE, profile)
139 return False 140 return False
140 return True 141 return True
146 """ 147 """
147 if not self.host.memory.getParamA(PARAM_NAME, PARAM_KEY, profile_key=profile): 148 if not self.host.memory.getParamA(PARAM_NAME, PARAM_KEY, profile_key=profile):
148 return True 149 return True
149 150
150 from_jid = JID(message.getAttribute("from")) 151 from_jid = JID(message.getAttribute("from"))
151 try: 152 if self.__isMUC(from_jid, profile):
152 domish.generateElementsNamed(message.elements(), name="body").next() 153 from_jid = from_jid.userhostJID()
154 else: # update entity data for one2one chat
155 assert(from_jid.resource)
153 try: 156 try:
154 domish.generateElementsNamed(message.elements(), name="active").next() 157 domish.generateElementsNamed(message.elements(), name="body").next()
155 # contact enabled Chat State Notifications 158 try:
156 self.updateEntityData(from_jid, True, profile) 159 domish.generateElementsNamed(message.elements(), name="active").next()
160 # contact enabled Chat State Notifications
161 self.updateEntityData(from_jid, True, profile)
162 except StopIteration:
163 if message.getAttribute('type') == 'chat':
164 # contact didn't enable Chat State Notifications
165 self.updateEntityData(from_jid, False, profile)
166 return True
157 except StopIteration: 167 except StopIteration:
158 if message.getAttribute('type') == 'chat': 168 pass
159 # contact didn't enable Chat State Notifications
160 self.updateEntityData(from_jid, False, profile)
161 return True
162 except StopIteration:
163 pass
164 169
165 # send our next "composing" states to any MUC and to the contacts who enabled the feature 170 # send our next "composing" states to any MUC and to the contacts who enabled the feature
166 self.__chatStateInit(from_jid, message.getAttribute("type"), profile) 171 self.__chatStateInit(from_jid, message.getAttribute("type"), profile)
167 172
168 state_list = [child.name for child in message.elements() if 173 state_list = [child.name for child in message.elements() if
189 try: 194 try:
190 # message with a body always mean active state 195 # message with a body always mean active state
191 domish.generateElementsNamed(message.elements(), name="body").next() 196 domish.generateElementsNamed(message.elements(), name="body").next()
192 message.addElement('active', NS_CHAT_STATES) 197 message.addElement('active', NS_CHAT_STATES)
193 # launch the chat state machine (init the timer) 198 # launch the chat state machine (init the timer)
199 if self.__isMUC(to_jid, profile):
200 to_jid = to_jid.userhostJID()
194 self.__chatStateActive(to_jid, mess_data["type"], profile) 201 self.__chatStateActive(to_jid, mess_data["type"], profile)
195 except StopIteration: 202 except StopIteration:
196 if "chat_state" in mess_data["extra"]: 203 if "chat_state" in mess_data["extra"]:
197 state = mess_data["extra"].pop("chat_state") 204 state = mess_data["extra"].pop("chat_state")
198 assert(state in CHAT_STATES) 205 assert(state in CHAT_STATES)
200 return mess_data 207 return mess_data
201 208
202 post_xml_treatments.addCallback(treatment) 209 post_xml_treatments.addCallback(treatment)
203 return True 210 return True
204 211
212 def __isMUC(self, to_jid, profile):
213 """Tell if that JID is a MUC or not
214
215 @param to_jid (JID): full or bare JID to check
216 @param profile (str): %(doc_profile)s
217 @return: bool
218 """
219 try:
220 type_ = self.host.memory.getEntityDatum(to_jid.userhostJID(), 'type', profile)
221 if type_ == 'chatroom':
222 return True
223 except (exceptions.UnknownEntityError, KeyError):
224 pass
225 return False
226
205 def __checkActivation(self, to_jid, forceEntityData, profile): 227 def __checkActivation(self, to_jid, forceEntityData, profile):
206 """ 228 """
207 @param to_joid: the contact's JID 229 @param to_jid: the contact's full JID (or bare if you know it's a MUC)
208 @param forceEntityData: if set to True, a non-existing 230 @param forceEntityData: if set to True, a non-existing
209 entity data will be considered to be True (and initialized) 231 entity data will be considered to be True (and initialized)
210 @param: current profile 232 @param: current profile
211 @return: True if the notifications should be sent to this JID. 233 @return: True if the notifications should be sent to this JID.
212 """ 234 """
213 # check if the parameter is active 235 # check if the parameter is active
214 if not self.host.memory.getParamA(PARAM_NAME, PARAM_KEY, profile_key=profile): 236 if not self.host.memory.getParamA(PARAM_NAME, PARAM_KEY, profile_key=profile):
215 return False 237 return False
216 # check if notifications should be sent to this contact 238 # check if notifications should be sent to this contact
239 if self.__isMUC(to_jid, profile):
240 return True
241 assert(to_jid.resource or not self.host.memory.isContactConnected(to_jid, profile))
217 try: 242 try:
218 type_ = self.host.memory.getEntityData(to_jid, ['type'], profile)['type'] 243 return self.host.memory.getEntityDatum(to_jid, ENTITY_KEY, profile)
219 if type_ == 'groupchat': # always send to groupchat
220 return True
221 except (exceptions.UnknownEntityError, KeyError):
222 pass # private chat
223 try:
224 return self.host.memory.getEntityData(to_jid, [ENTITY_KEY], profile)[ENTITY_KEY]
225 except (exceptions.UnknownEntityError, KeyError): 244 except (exceptions.UnknownEntityError, KeyError):
226 if forceEntityData: 245 if forceEntityData:
227 # enable it for the first time 246 # enable it for the first time
228 self.updateEntityData(to_jid, True, profile) 247 self.updateEntityData(to_jid, True, profile)
229 return True 248 return True
231 return False 250 return False
232 251
233 def __chatStateInit(self, to_jid, mess_type, profile): 252 def __chatStateInit(self, to_jid, mess_type, profile):
234 """ 253 """
235 Data initialization for the chat state machine. 254 Data initialization for the chat state machine.
236 """ 255
237 # TODO: use also the resource in map key (not for groupchat) 256 @param to_jid (JID): full JID for one2one, bare JID for MUC
238 to_jid = to_jid.userhostJID() 257 @param mess_type (str): "one2one" or "groupchat"
258 @param profile (str): %(doc_profile)s
259 """
239 if mess_type is None: 260 if mess_type is None:
240 return 261 return
241 if not hasattr(self, "map"): 262 if not hasattr(self, "map"):
242 self.map = {} 263 self.map = {}
243 profile_map = self.map.setdefault(profile, {}) 264 profile_map = self.map.setdefault(profile, {})
244 if not to_jid in profile_map: 265 if to_jid not in profile_map:
245 machine = ChatStateMachine(self.host, to_jid, 266 machine = ChatStateMachine(self.host, to_jid,
246 mess_type, profile) 267 mess_type, profile)
247 self.map[profile][to_jid] = machine 268 self.map[profile][to_jid] = machine
248 269
249 def __chatStateActive(self, to_jid, mess_type, profile_key): 270 def __chatStateActive(self, to_jid, mess_type, profile_key):
250 """ 271 """
251 Launch the chat state machine on "active" state. 272 Launch the chat state machine on "active" state.
252 """ 273
253 # TODO: use also the JID resource in the map key (not for groupchat) 274 @param to_jid (JID): full JID for one2one, bare JID for MUC
254 to_jid = to_jid.userhostJID() 275 @param mess_type (str): "one2one" or "groupchat"
276 @param profile (str): %(doc_profile)s
277 """
255 profile = self.host.memory.getProfileName(profile_key) 278 profile = self.host.memory.getProfileName(profile_key)
256 if profile is None: 279 if profile is None:
257 raise exceptions.ProfileUnknownError 280 raise exceptions.ProfileUnknownError
258 return
259 self.__chatStateInit(to_jid, mess_type, profile) 281 self.__chatStateInit(to_jid, mess_type, profile)
260 self.map[profile][to_jid]._onEvent("active") 282 self.map[profile][to_jid]._onEvent("active")
261 283
262 def chatStateComposing(self, to_jid_s, profile_key): 284 def chatStateComposing(self, to_jid_s, profile_key):
263 """ 285 """Move to the "composing" state when required.
264 Move to the "composing" state. Since this method is called 286
265 from the front-end, it needs to check the values of the 287 Since this method is called from the front-end, it needs to check the
266 parameter "Send chat state notifications" and the entity 288 values of the parameter "Send chat state notifications" and the entity
267 data associated to the target JID. 289 data associated to the target JID.
268 TODO: try to optimize this method which is called often 290
269 """ 291 @param to_jid_s (str): contact full JID as a string
270 # TODO: use also the JID resource in the map key (not for groupchat) 292 @param profile_key (str): %(doc_profile_key)s
271 to_jid = JID(to_jid_s).userhostJID() 293 """
294 # TODO: try to optimize this method which is called often
272 profile = self.host.memory.getProfileName(profile_key) 295 profile = self.host.memory.getProfileName(profile_key)
273 if profile is None: 296 if profile is None:
274 raise exceptions.ProfileUnknownError 297 raise exceptions.ProfileUnknownError
275 return 298 to_jid = JID(to_jid_s)
299 if self.__isMUC(to_jid, profile):
300 to_jid = to_jid.userhostJID()
301 elif not to_jid.resource:
302 to_jid.resource = self.host.memory.getLastResource(to_jid, profile)
276 if not self.__checkActivation(to_jid, forceEntityData=False, profile=profile): 303 if not self.__checkActivation(to_jid, forceEntityData=False, profile=profile):
277 return 304 return
278 try: 305 try:
279 self.map[profile][to_jid]._onEvent("composing") 306 self.map[profile][to_jid]._onEvent("composing")
280 except (KeyError, AttributeError): 307 except (KeyError, AttributeError):
316 # send a new message without body 343 # send a new message without body
317 self.host.sendMessage(self.to_jid, '', '', self.mess_type, 344 self.host.sendMessage(self.to_jid, '', '', self.mess_type,
318 extra={"chat_state": state}, 345 extra={"chat_state": state},
319 profile_key=self.profile) 346 profile_key=self.profile)
320 self.state = state 347 self.state = state
321 if not self.timer is None: 348 if self.timer is not None:
322 self.timer.cancel() 349 self.timer.cancel()
323 350
324 if not state in TRANSITIONS: 351 if state not in TRANSITIONS:
325 return 352 return
326 if not "next_state" in TRANSITIONS[state]: 353 if "next_state" not in TRANSITIONS[state]:
327 return 354 return
328 if not "delay" in TRANSITIONS[state]: 355 if "delay" not in TRANSITIONS[state]:
329 return 356 return
330 next_state = TRANSITIONS[state]["next_state"] 357 next_state = TRANSITIONS[state]["next_state"]
331 delay = TRANSITIONS[state]["delay"] 358 delay = TRANSITIONS[state]["delay"]
332 if next_state == "" or delay < 0: 359 if next_state == "" or delay < 0:
333 return 360 return