Mercurial > libervia-backend
changeset 3060:730bbed77a89
plugin XEP-0045: join / MAM history improvements:
- fixed use of history_d deferred so MAM history is stored in database as soon as possible
and in right order
- get MAM history with pages of 20 messages instead of 100
- if a message can't be parsed, an error message is logged (with a dump of the stanza),
and the message is ignored, avoiding a crash and the impossibility the join the room at
all
- a new `mucRoomPrepareJoin` signal, containing room jid and profile, is sent when a room
joining is started, this allow better UX as the frontend can show the room immediately,
and lock it with a waiting message until it is fully available. The `mucRoomJoined` is
still sent when the room is fully joined/avaiable, meaning that the frontend can unlock
it and let the user interact with it
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 18 Oct 2019 14:45:07 +0200 |
parents | aa728dc7b0ce |
children | 948833e3b542 |
files | sat/plugins/plugin_xep_0045.py |
diffstat | 1 files changed, 73 insertions(+), 25 deletions(-) [+] |
line wrap: on
line diff
--- a/sat/plugins/plugin_xep_0045.py Fri Oct 18 14:36:46 2019 +0200 +++ b/sat/plugins/plugin_xep_0045.py Fri Oct 18 14:45:07 2019 +0200 @@ -86,24 +86,61 @@ log.info(_("Plugin XEP_0045 initialization")) self.host = host self._sessions = memory.Sessions() - host.bridge.addMethod("mucJoin", ".plugin", in_sign='ssa{ss}s', out_sign='(bsa{sa{ss}}sss)', method=self._join, async_=True) # return same arguments as mucRoomJoined + a boolean set to True is the room was already joined (first argument) - host.bridge.addMethod("mucNick", ".plugin", in_sign='sss', out_sign='', method=self._nick) - host.bridge.addMethod("mucNickGet", ".plugin", in_sign='ss', out_sign='s', method=self._getRoomNick) - host.bridge.addMethod("mucLeave", ".plugin", in_sign='ss', out_sign='', method=self._leave, async_=True) - host.bridge.addMethod("mucOccupantsGet", ".plugin", in_sign='ss', out_sign='a{sa{ss}}', method=self._getRoomOccupants) - host.bridge.addMethod("mucSubject", ".plugin", in_sign='sss', out_sign='', method=self._subject) - host.bridge.addMethod("mucGetRoomsJoined", ".plugin", in_sign='s', out_sign='a(sa{sa{ss}}ss)', method=self._getRoomsJoined) - host.bridge.addMethod("mucGetUniqueRoomName", ".plugin", in_sign='ss', out_sign='s', method=self._getUniqueName) - host.bridge.addMethod("mucConfigureRoom", ".plugin", in_sign='ss', out_sign='s', method=self._configureRoom, async_=True) - host.bridge.addMethod("mucGetDefaultService", ".plugin", in_sign='', out_sign='s', method=self.getDefaultMUC) - host.bridge.addMethod("mucGetService", ".plugin", in_sign='ss', out_sign='s', method=self._getMUCService, async_=True) - host.bridge.addSignal("mucRoomJoined", ".plugin", signature='sa{sa{ss}}sss') # args: room_jid, occupants, user_nick, subject, profile - host.bridge.addSignal("mucRoomLeft", ".plugin", signature='ss') # args: room_jid, profile - host.bridge.addSignal("mucRoomUserChangedNick", ".plugin", signature='ssss') # args: room_jid, old_nick, new_nick, profile - host.bridge.addSignal("mucRoomNewSubject", ".plugin", signature='sss') # args: room_jid, subject, profile - self.__submit_conf_id = host.registerCallback(self._submitConfiguration, with_data=True) + # return same arguments as mucRoomJoined + a boolean set to True is the room was + # already joined (first argument) + host.bridge.addMethod( + "mucJoin", ".plugin", in_sign='ssa{ss}s', out_sign='(bsa{sa{ss}}sss)', + method=self._join, async_=True) + host.bridge.addMethod( + "mucNick", ".plugin", in_sign='sss', out_sign='', method=self._nick) + host.bridge.addMethod( + "mucNickGet", ".plugin", in_sign='ss', out_sign='s', method=self._getRoomNick) + host.bridge.addMethod( + "mucLeave", ".plugin", in_sign='ss', out_sign='', method=self._leave, + async_=True) + host.bridge.addMethod( + "mucOccupantsGet", ".plugin", in_sign='ss', out_sign='a{sa{ss}}', + method=self._getRoomOccupants) + host.bridge.addMethod( + "mucSubject", ".plugin", in_sign='sss', out_sign='', method=self._subject) + host.bridge.addMethod( + "mucGetRoomsJoined", ".plugin", in_sign='s', out_sign='a(sa{sa{ss}}ss)', + method=self._getRoomsJoined) + host.bridge.addMethod( + "mucGetUniqueRoomName", ".plugin", in_sign='ss', out_sign='s', + method=self._getUniqueName) + host.bridge.addMethod( + "mucConfigureRoom", ".plugin", in_sign='ss', out_sign='s', + method=self._configureRoom, async_=True) + host.bridge.addMethod( + "mucGetDefaultService", ".plugin", in_sign='', out_sign='s', + method=self.getDefaultMUC) + host.bridge.addMethod( + "mucGetService", ".plugin", in_sign='ss', out_sign='s', + method=self._getMUCService, async_=True) + # called when a room will be joined but must be locked until join is received + # (room is prepared, history is getting retrieved) + # args: room_jid, profile + host.bridge.addSignal( + "mucRoomPrepareJoin", ".plugin", signature='ss') + # args: room_jid, occupants, user_nick, subject, profile + host.bridge.addSignal( + "mucRoomJoined", ".plugin", signature='sa{sa{ss}}sss') + # args: room_jid, profile + host.bridge.addSignal( + "mucRoomLeft", ".plugin", signature='ss') + # args: room_jid, old_nick, new_nick, profile + host.bridge.addSignal( + "mucRoomUserChangedNick", ".plugin", signature='ssss') + # args: room_jid, subject, profile + host.bridge.addSignal( + "mucRoomNewSubject", ".plugin", signature='sss') + self.__submit_conf_id = host.registerCallback( + self._submitConfiguration, with_data=True) self._room_join_id = host.registerCallback(self._UIRoomJoinCb, with_data=True) - host.importMenu((D_("MUC"), D_("configure")), self._configureRoomMenu, security_limit=0, help_string=D_("Configure Multi-User Chat room"), type_=C.MENU_ROOM) + host.importMenu( + (D_("MUC"), D_("configure")), self._configureRoomMenu, security_limit=0, + help_string=D_("Configure Multi-User Chat room"), type_=C.MENU_ROOM) try: self.text_cmds = self.host.plugins[C.TEXT_CMDS] except KeyError: @@ -487,6 +524,7 @@ return defer.fail(AlreadyJoined(room)) log.info(_("[{profile}] is joining room {room} with nick {nick}").format( profile=client.profile, room=room_jid.userhost(), nick=nick)) + self.host.bridge.mucRoomPrepareJoin(room_jid.userhost(), client.profile) password = options.get("password") @@ -911,7 +949,10 @@ # we don't want any history from room as we'll get it with MAM room_jid, nick, muc.HistoryOptions(maxStanzas=0), password=password) room._history_type = HISTORY_MAM - room._history_d = defer.Deferred() + history_d = room._history_d = defer.Deferred() + # we trigger now the deferred so all callback are processed as soon as possible + # and in order + history_d.callback(None) last_mess = yield self.host.memory.historyGet( room_jid, @@ -924,7 +965,7 @@ profile=client.profile) if last_mess: stanza_id = last_mess[0][-1]['stanza_id'] - rsm_req = rsm.RSMRequest(max_=100, after=stanza_id) + rsm_req = rsm.RSMRequest(max_=20, after=stanza_id) no_loop=False else: log.info("We have no MAM archive for room {room_jid}.".format( @@ -961,7 +1002,14 @@ 'Forwarded message element has a "to" attribute while it is ' 'forbidden by specifications') fwd_message_elt["to"] = client.jid.full() - mess_data = client.messageProt.parseMessage(fwd_message_elt) + try: + mess_data = client.messageProt.parseMessage(fwd_message_elt) + except Exception as e: + log.error( + f"Can't parse message, ignoring it: {e}\n" + f"{fwd_message_elt.toXml()}" + ) + continue # we attache parsed message data to element, to avoid parsing # again in _addToHistory fwd_message_elt._mess_data = mess_data @@ -981,11 +1029,11 @@ # the order is different (we have to join then get MAM archive, so subject # is received before archive), so we change state and add the callbacks here. self.changeRoomState(room, ROOM_STATE_LIVE) - room._history_d.addCallbacks(self._historyCb, self._historyEb, [room], + history_d.addCallbacks(self._historyCb, self._historyEb, [room], errbackArgs=[room]) - # callback is done now that all needed Deferred have been added to _history_d - room._history_d.callback(None) + # we wait for all callbacks to be processed + yield history_d defer.returnValue(room) @@ -1243,8 +1291,8 @@ There are a few event methods that may get called here. L{receivedGroupChat}, L{receivedSubject} or L{receivedHistory}. """ - # We override this method to fix subject handling - # FIXME: remove this merge fixed upstream + # We override this method to fix subject handling (empty strings were discarded) + # FIXME: remove this once fixed upstream room, user = self._getRoomUser(message) if room is None: