Mercurial > libervia-backend
comparison sat/core/xmpp.py @ 2701:2ea2369ae7de
plugin XEP-0313: implementation of MAM for messages:
- (core/xmpp): new messageGetBridgeArgs to easily retrieve arguments used in bridge from message data
- : parseMessage is not static anymore
- : new "message_parse" trigger point
- (xep-0313) : new "MAMGet" bridge method to retrieve history from MAM instead of local one
- : on profileConnected, if previous MAM message is found (i.e. message with a stanza_id), message received while offline are retrieved and injected in message workflow.
In other words, one2one history is synchronised on connection.
- : new "parseExtra" method which parse MAM (and optionally RSM) option from extra dictionary used in bridge.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 01 Dec 2018 10:33:43 +0100 |
parents | a8ec00731ce7 |
children | 57eac4fd0ec0 |
comparison
equal
deleted
inserted
replaced
2700:035901dc946d | 2701:2ea2369ae7de |
---|---|
58 # else, it's a deferred which fire on disconnection | 58 # else, it's a deferred which fire on disconnection |
59 self._connected_d = None | 59 self._connected_d = None |
60 self.profile = profile | 60 self.profile = profile |
61 self.host_app = host_app | 61 self.host_app = host_app |
62 self.cache = cache.Cache(host_app, profile) | 62 self.cache = cache.Cache(host_app, profile) |
63 self._mess_id_uid = {} # map from message id to uid used in history. | 63 self.mess_id2uid = {} # map from message id to uid used in history. |
64 # Key: (full_jid,message_id) Value: uid | 64 # Key: (full_jid, message_id) Value: uid |
65 # this Deferred fire when entity is connected | 65 # this Deferred fire when entity is connected |
66 self.conn_deferred = defer.Deferred() | 66 self.conn_deferred = defer.Deferred() |
67 self._progress_cb = {} # callback called when a progress is requested | 67 self._progress_cb = {} # callback called when a progress is requested |
68 # (key = progress id) | 68 # (key = progress id) |
69 self.actions = {} # used to keep track of actions for retrieval (key = action_id) | 69 self.actions = {} # used to keep track of actions for retrieval (key = action_id) |
538 log.warning( | 538 log.warning( |
539 u"No message found" | 539 u"No message found" |
540 ) # empty body should be managed by plugins before this point | 540 ) # empty body should be managed by plugins before this point |
541 return data | 541 return data |
542 | 542 |
543 def messageGetBridgeArgs(self, data): | |
544 """Generate args to use with bridge from data dict""" | |
545 return (data[u"uid"], data[u"timestamp"], data[u"from"].full(), | |
546 data[u"to"].full(), data[u"message"], data[u"subject"], | |
547 data[u"type"], data[u"extra"]) | |
548 | |
549 | |
543 def messageSendToBridge(self, data): | 550 def messageSendToBridge(self, data): |
544 """Send message to bridge, so frontends can display it | 551 """Send message to bridge, so frontends can display it |
545 | 552 |
546 @param data: message data dictionnary | 553 @param data: message data dictionnary |
547 @param client: profile's client | 554 @param client: profile's client |
548 """ | 555 """ |
549 if data[u"type"] != C.MESS_TYPE_GROUPCHAT: | 556 if data[u"type"] != C.MESS_TYPE_GROUPCHAT: |
550 # we don't send groupchat message to bridge, as we get them back | 557 # we don't send groupchat message to bridge, as we get them back |
551 # and they will be added the | 558 # and they will be added the |
552 if ( | 559 if (data[u"message"] or data[u"subject"]): # we need a message to send |
553 data[u"message"] or data[u"subject"] | 560 # something |
554 ): # we need a message to send something | 561 |
555 # We send back the message, so all frontends are aware of it | 562 # We send back the message, so all frontends are aware of it |
556 self.host_app.bridge.messageNew( | 563 self.host_app.bridge.messageNew( |
557 data[u"uid"], | 564 *self.messageGetBridgeArgs(data), |
558 data[u"timestamp"], | 565 profile=self.profile |
559 data[u"from"].full(), | |
560 data[u"to"].full(), | |
561 data[u"message"], | |
562 data[u"subject"], | |
563 data[u"type"], | |
564 data[u"extra"], | |
565 profile=self.profile, | |
566 ) | 566 ) |
567 else: | 567 else: |
568 log.warning(_(u"No message found")) | 568 log.warning(_(u"No message found")) |
569 return data | 569 return data |
570 | 570 |
841 class SatMessageProtocol(xmppim.MessageProtocol): | 841 class SatMessageProtocol(xmppim.MessageProtocol): |
842 def __init__(self, host): | 842 def __init__(self, host): |
843 xmppim.MessageProtocol.__init__(self) | 843 xmppim.MessageProtocol.__init__(self) |
844 self.host = host | 844 self.host = host |
845 | 845 |
846 @staticmethod | 846 def parseMessage(self, message_elt): |
847 def parseMessage(message_elt, client=None): | |
848 """parse a message XML and return message_data | 847 """parse a message XML and return message_data |
849 | 848 |
850 @param message_elt(domish.Element): raw <message> xml | 849 @param message_elt(domish.Element): raw <message> xml |
851 @param client(SatXMPPClient, None): client to map message id to uid | 850 @param client(SatXMPPClient, None): client to map message id to uid |
852 if None, mapping will not be done | 851 if None, mapping will not be done |
853 @return(dict): message data | 852 @return(dict): message data |
854 """ | 853 """ |
854 if message_elt.name != u"message": | |
855 log.warning(_( | |
856 u"parseMessage used with a non <message/> stanza, ignoring: {xml}" | |
857 .format(xml=message_elt.toXml()))) | |
858 return {} | |
859 client = self.parent | |
855 message = {} | 860 message = {} |
856 subject = {} | 861 subject = {} |
857 extra = {} | 862 extra = {} |
858 data = { | 863 data = { |
859 "from": jid.JID(message_elt["from"]), | 864 u"from": jid.JID(message_elt["from"]), |
860 "to": jid.JID(message_elt["to"]), | 865 u"to": jid.JID(message_elt["to"]), |
861 "uid": message_elt.getAttribute( | 866 u"uid": message_elt.getAttribute( |
862 "uid", unicode(uuid.uuid4()) | 867 u"uid", unicode(uuid.uuid4()) |
863 ), # XXX: uid is not a standard attribute but may be added by plugins | 868 ), # XXX: uid is not a standard attribute but may be added by plugins |
864 "message": message, | 869 u"message": message, |
865 "subject": subject, | 870 u"subject": subject, |
866 "type": message_elt.getAttribute("type", "normal"), | 871 u"type": message_elt.getAttribute(u"type", u"normal"), |
867 "extra": extra, | 872 u"extra": extra, |
868 } | 873 } |
869 | 874 |
870 if client is not None: | 875 try: |
871 try: | 876 message_id = data[u"extra"][u"message_id"] = message_elt[u"id"] |
872 data["stanza_id"] = message_elt["id"] | 877 except KeyError: |
873 except KeyError: | 878 pass |
874 pass | 879 else: |
875 else: | 880 client.mess_id2uid[(data["from"], message_id)] = data["uid"] |
876 client._mess_id_uid[(data["from"], data["stanza_id"])] = data["uid"] | |
877 | 881 |
878 # message | 882 # message |
879 for e in message_elt.elements(C.NS_CLIENT, "body"): | 883 for e in message_elt.elements(C.NS_CLIENT, "body"): |
880 message[e.getAttribute((C.NS_XML, "lang"), "")] = unicode(e) | 884 message[e.getAttribute((C.NS_XML, "lang"), "")] = unicode(e) |
881 | 885 |
892 parsed_delay = delay.Delay.fromElement(delay_elt) | 896 parsed_delay = delay.Delay.fromElement(delay_elt) |
893 data["timestamp"] = calendar.timegm(parsed_delay.stamp.utctimetuple()) | 897 data["timestamp"] = calendar.timegm(parsed_delay.stamp.utctimetuple()) |
894 data["received_timestamp"] = unicode(time.time()) | 898 data["received_timestamp"] = unicode(time.time()) |
895 if parsed_delay.sender: | 899 if parsed_delay.sender: |
896 data["delay_sender"] = parsed_delay.sender.full() | 900 data["delay_sender"] = parsed_delay.sender.full() |
901 | |
902 self.host.trigger.point("message_parse", client, message_elt, data) | |
897 return data | 903 return data |
898 | 904 |
899 def _onMessageStartWorkflow(self, cont, client, message_elt, post_treat): | 905 def _onMessageStartWorkflow(self, cont, client, message_elt, post_treat): |
900 """Parse message and do post treatments | 906 """Parse message and do post treatments |
901 | 907 |
905 may have be modified by triggers | 911 may have be modified by triggers |
906 @param post_treat(defer.Deferred): post parsing treatments | 912 @param post_treat(defer.Deferred): post parsing treatments |
907 """ | 913 """ |
908 if not cont: | 914 if not cont: |
909 return | 915 return |
910 data = self.parseMessage(message_elt, client=client) | 916 data = self.parseMessage(message_elt) |
911 post_treat.addCallback(self.skipEmptyMessage) | 917 post_treat.addCallback(self.skipEmptyMessage) |
912 post_treat.addCallback(self.addToHistory, client) | 918 post_treat.addCallback(self.addToHistory, client) |
913 post_treat.addCallback(self.bridgeSignal, client, data) | 919 post_treat.addCallback(self.bridgeSignal, client, data) |
914 post_treat.addErrback(self.cancelErrorTrap) | 920 post_treat.addErrback(self.cancelErrorTrap) |
915 post_treat.callback(data) | 921 post_treat.callback(data) |