Mercurial > libervia-backend
diff src/core/sat_main.py @ 1052:e88bff4c8b77
core (XMPP): sendMessage refactoring:
- better separation of message sending actions
- use of more generic exceptions to hook the behaviour (SkipHistory and CancelError)
- use of raise instead of return
- use of failure.trap
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 07 Jun 2014 16:35:29 +0200 |
parents | 066308706dc6 |
children | b2b9c184033f |
line wrap: on
line diff
--- a/src/core/sat_main.py Tue Jun 03 17:10:12 2014 +0200 +++ b/src/core/sat_main.py Sat Jun 07 16:35:29 2014 +0200 @@ -54,22 +54,6 @@ return "sat_id_" + str(sat_id) -class MessageSentAndStored(Exception): - """ Exception to raise if the message has been already sent and stored in the - history by the trigger, so the rest of the process should be stopped. This - should normally be raised by the trigger with the minimal priority """ - def __init__(self, reason, mess_data): - Exception.__init__(self, reason) - self.mess_data = mess_data # added for testing purpose - - -class AbortSendMessage(Exception): - """ Exception to raise if sending the message should be aborted. This can be - raised by any trigger but a side action should be planned by the trigger - to inform the user about what happened """ - pass - - class SAT(service.Service): @property @@ -433,7 +417,7 @@ return self.profiles[profile].isConnected() - ## jabber methods ## + ## XMPP methods ## def getWaitingConf(self, profile_key=None): assert(profile_key) @@ -444,6 +428,18 @@ ret.append((conf_id, conf_type, data)) return ret + def generateMessageXML(self, mess_data): + mess_data['xml'] = domish.Element((None, 'message')) + mess_data['xml']["to"] = mess_data["to"].full() + mess_data['xml']["from"] = mess_data['from'].full() + mess_data['xml']["type"] = mess_data["type"] + mess_data['xml']['id'] = str(uuid4()) + if mess_data["subject"]: + mess_data['xml'].addElement("subject", None, mess_data['subject']) + if mess_data["message"]: # message without body are used to send chat states + mess_data['xml'].addElement("body", None, mess_data["message"]) + return mess_data + def _sendMessage(self, to_s, msg, subject=None, mess_type='auto', extra={}, profile_key=C.PROF_KEY_NONE): to_jid = jid.JID(to_s) #XXX: we need to use the dictionary comprehension because D-Bus return its own types, and pickle can't manage them. TODO: Need to find a better way @@ -454,11 +450,11 @@ profile = self.memory.getProfileName(profile_key) assert(profile) client = self.profiles[profile] - current_jid = client.jid if extra is None: extra = {} mess_data = { # we put data in a dict, so trigger methods can change them "to": to_jid, + "from": client.jid, "message": msg, "subject": subject, "type": mess_type, @@ -493,67 +489,67 @@ log.debug(_("Sending jabber message of type [%(type)s] to %(to)s...") % {"type": mess_data["type"], "to": to_jid.full()}) - def generateXML(mess_data): - mess_data['xml'] = domish.Element((None, 'message')) - mess_data['xml']["to"] = mess_data["to"].full() - mess_data['xml']["from"] = current_jid.full() - mess_data['xml']["type"] = mess_data["type"] - if mess_data["subject"]: - mess_data['xml'].addElement("subject", None, subject) - # message without body are used to send chat states - if mess_data["message"]: - mess_data['xml'].addElement("body", None, mess_data["message"]) - return mess_data + def cancelErrorTrap(failure): + """A message sending can be cancelled by a plugin treatment""" + failure.trap(exceptions.CancelError) - def sendErrback(e): - text = '%s: %s' % (e.value.__class__.__name__, e.getErrorMessage()) - if e.check(MessageSentAndStored): - log.debug(text) - elif e.check(AbortSendMessage): - log.warning(text) - return e - else: - log.error("Unmanaged exception: %s" % text) - return e - pre_xml_treatments.addCallback(generateXML) + pre_xml_treatments.addCallback(lambda dummy: self.generateMessageXML(mess_data)) pre_xml_treatments.chainDeferred(post_xml_treatments) - post_xml_treatments.addCallback(self.sendAndStoreMessage, False, profile) - post_xml_treatments.addErrback(sendErrback) + post_xml_treatments.addCallback(self._sendMessageToStream, client) + post_xml_treatments.addCallback(self._storeMessage, client) + post_xml_treatments.addCallback(self.sendMessageToBridge, client) + post_xml_treatments.addErrback(cancelErrorTrap) pre_xml_treatments.callback(mess_data) return pre_xml_treatments - def sendAndStoreMessage(self, mess_data, skip_send=False, profile=None): - """Actually send and store the message to history, after all the treatments have been done. - This has been moved outside the main sendMessage method because it is used by XEP-0033 to complete a server-side feature not yet - implemented by the prosody plugin. - @param mess_data: message data dictionary - @param skip_send: set to True to skip sending the message to only store it - @param profile: profile + def _sendMessageToStream(self, mess_data, client): + """Actualy send the message to the server + + @param mess_data: message data dictionnary + @param client: profile's client """ - try: - client = self.profiles[profile] - except KeyError: - log.error(_("Trying to send a message with no profile")) - return - current_jid = client.jid - if not skip_send: - client.xmlstream.send(mess_data['xml']) + client.xmlstream.send(mess_data['xml']) + return mess_data + + def _storeMessage(self, mess_data, client): + """Store message into database (for local history) + + @param mess_data: message data dictionnary + @param client: profile's client + """ if mess_data["type"] != "groupchat": # we don't add groupchat message to history, as we get them back # and they will be added then if mess_data['message']: # we need a message to save something - self.memory.addToHistory(current_jid, mess_data['to'], + self.memory.addToHistory(client.jid, mess_data['to'], unicode(mess_data["message"]), unicode(mess_data["type"]), mess_data['extra'], - profile=profile) + profile=client.profile) + else: + log.warning(_("No message found")) # empty body should be managed by plugins before this point + return mess_data + + def sendMessageToBridge(self, mess_data, client): + """Send message to bridge, so frontends can display it + + @param mess_data: message data dictionnary + @param client: profile's client + """ + if mess_data["type"] != "groupchat": + # we don't send groupchat message back to bridge, as we get them back + # and they will be added the + if mess_data['message']: # we need a message to save something # We send back the message, so all clients are aware of it - self.bridge.newMessage(mess_data['xml']['from'], + self.bridge.newMessage(mess_data['from'].full(), unicode(mess_data["message"]), mess_type=mess_data["type"], - to_jid=mess_data['xml']['to'], + to_jid=mess_data['to'].full(), extra=mess_data['extra'], - profile=profile) + profile=client.profile) + else: + log.warning(_("No message found")) + return mess_data def _setPresence(self, to="", show="", statuses=None, profile_key=C.PROF_KEY_NONE): return self.setPresence(jid.JID(to) if to else None, show, statuses, profile_key)