comparison src/plugins/plugin_xep_0085.py @ 1253:c13a46207410

plugin XEP-0085: send 'gone' state before disconnection
author souliane <souliane@mailoo.org>
date Mon, 20 Oct 2014 20:26:46 +0200
parents 5ff9f9af9d1f
children 60dfa2f5d61f
comparison
equal deleted inserted replaced
1252:5ff9f9af9d1f 1253:c13a46207410
92 } 92 }
93 93
94 def __init__(self, host): 94 def __init__(self, host):
95 log.info(_("Chat State Notifications plugin initialization")) 95 log.info(_("Chat State Notifications plugin initialization"))
96 self.host = host 96 self.host = host
97 self.map = {}
97 98
98 # parameter value is retrieved before each use 99 # parameter value is retrieved before each use
99 host.memory.updateParams(self.params) 100 host.memory.updateParams(self.params)
100 101
101 # triggers from core 102 # triggers from core
102 host.trigger.add("MessageReceived", self.messageReceivedTrigger) 103 host.trigger.add("MessageReceived", self.messageReceivedTrigger)
103 host.trigger.add("sendMessage", self.sendMessageTrigger) 104 host.trigger.add("sendMessage", self.sendMessageTrigger)
104 host.trigger.add("paramUpdateTrigger", self.paramUpdateTrigger) 105 host.trigger.add("paramUpdateTrigger", self.paramUpdateTrigger)
105 # TODO: handle profile disconnection (free memory in entity data)
106 106
107 # args: to_s (jid as string), profile 107 # args: to_s (jid as string), profile
108 host.bridge.addMethod("chatStateComposing", ".plugin", in_sign='ss', 108 host.bridge.addMethod("chatStateComposing", ".plugin", in_sign='ss',
109 out_sign='', method=self.chatStateComposing) 109 out_sign='', method=self.chatStateComposing)
110 110
111 # args: from (jid as string), state in CHAT_STATES, profile 111 # args: from (jid as string), state in CHAT_STATES, profile
112 host.bridge.addSignal("chatStateReceived", ".plugin", signature='sss') 112 host.bridge.addSignal("chatStateReceived", ".plugin", signature='sss')
113 113
114 def getHandler(self, profile): 114 def getHandler(self, profile):
115 return XEP_0085_handler(self, profile) 115 return XEP_0085_handler(self, profile)
116
117 def profileDisconnected(self, profile):
118 """Eventually send a 'gone' state to all one2one contacts."""
119 if profile not in self.map:
120 return
121 for to_jid in self.map[profile]:
122 # FIXME: the "unavailable" presence stanza is received by to_jid
123 # before the chat state, so it will be ignored... find a way to
124 # actually defer the disconnection
125 self.map[profile][to_jid]._onEvent('gone')
126 del self.map[profile]
116 127
117 def updateEntityData(self, entity_jid, value, profile): 128 def updateEntityData(self, entity_jid, value, profile):
118 """ 129 """
119 Update the entity data of the given profile for one or all contacts. 130 Update the entity data of the given profile for one or all contacts.
120 Reset the chat state(s) display if the notification has been disabled. 131 Reset the chat state(s) display if the notification has been disabled.
257 @param mess_type (str): "one2one" or "groupchat" 268 @param mess_type (str): "one2one" or "groupchat"
258 @param profile (str): %(doc_profile)s 269 @param profile (str): %(doc_profile)s
259 """ 270 """
260 if mess_type is None: 271 if mess_type is None:
261 return 272 return
262 if not hasattr(self, "map"):
263 self.map = {}
264 profile_map = self.map.setdefault(profile, {}) 273 profile_map = self.map.setdefault(profile, {})
265 if to_jid not in profile_map: 274 if to_jid not in profile_map:
266 machine = ChatStateMachine(self.host, to_jid, 275 machine = ChatStateMachine(self.host, to_jid,
267 mess_type, profile) 276 mess_type, profile)
268 self.map[profile][to_jid] = machine 277 self.map[profile][to_jid] = machine
336 """ 345 """
337 Move to the specified state, eventually send the 346 Move to the specified state, eventually send the
338 notification to the contact (the "active" state is 347 notification to the contact (the "active" state is
339 automatically sent with each message) and set the timer. 348 automatically sent with each message) and set the timer.
340 """ 349 """
350 assert(state in TRANSITIONS)
351 transition = TRANSITIONS[state]
352 assert("next_state" in transition and "delay" in transition)
353
341 if state != self.state and state != "active": 354 if state != self.state and state != "active":
342 if state != 'gone' or self.mess_type != 'groupchat': 355 if state != 'gone' or self.mess_type != 'groupchat':
343 # send a new message without body 356 # send a new message without body
344 self.host.sendMessage(self.to_jid, '', '', self.mess_type, 357 log.debug(u"sending state '{state}' to {jid}".format(state=state, jid=self.to_jid.full()))
345 extra={"chat_state": state}, 358 client = self.host.getClient(self.profile)
346 profile_key=self.profile) 359 mess_data = {'message': None,
360 'type': self.mess_type,
361 'from': client.jid,
362 'to': self.to_jid,
363 'subject': None
364 }
365 self.host.generateMessageXML(mess_data)
366 mess_data['xml'].addElement(state, NS_CHAT_STATES)
367 client.xmlstream.send(mess_data['xml'])
368
347 self.state = state 369 self.state = state
348 if self.timer is not None: 370 if self.timer is not None:
349 self.timer.cancel() 371 self.timer.cancel()
350 372
351 if state not in TRANSITIONS: 373 if transition["next_state"] and transition["delay"] > 0:
352 return 374 self.timer = Timer(transition["delay"], self._onEvent, [transition["next_state"]])
353 if "next_state" not in TRANSITIONS[state]: 375 self.timer.start()
354 return
355 if "delay" not in TRANSITIONS[state]:
356 return
357 next_state = TRANSITIONS[state]["next_state"]
358 delay = TRANSITIONS[state]["delay"]
359 if next_state == "" or delay < 0:
360 return
361 self.timer = Timer(delay, self._onEvent, [next_state])
362 self.timer.start()
363 376
364 377
365 class XEP_0085_handler(XMPPHandler): 378 class XEP_0085_handler(XMPPHandler):
366 implements(iwokkel.IDisco) 379 implements(iwokkel.IDisco)
367 380