Mercurial > libervia-backend
diff libervia/backend/plugins/plugin_xep_0045.py @ 4270:0d7bb4df2343
Reformatted code base using black.
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 19 Jun 2024 18:44:57 +0200 |
parents | a7d4007a8fa5 |
children |
line wrap: on
line diff
--- a/libervia/backend/plugins/plugin_xep_0045.py Tue Jun 18 12:06:45 2024 +0200 +++ b/libervia/backend/plugins/plugin_xep_0045.py Wed Jun 19 18:44:57 2024 +0200 @@ -53,14 +53,14 @@ C.PI_RECOMMENDATIONS: [C.TEXT_CMDS, "XEP-0313"], C.PI_MAIN: "XEP_0045", C.PI_HANDLER: "yes", - C.PI_DESCRIPTION: _("""Implementation of Multi-User Chat""") + C.PI_DESCRIPTION: _("""Implementation of Multi-User Chat"""), } -NS_MUC = 'http://jabber.org/protocol/muc' -AFFILIATIONS = ('owner', 'admin', 'member', 'none', 'outcast') -ROOM_USER_JOINED = 'ROOM_USER_JOINED' -ROOM_USER_LEFT = 'ROOM_USER_LEFT' -OCCUPANT_KEYS = ('nick', 'entity', 'affiliation', 'role') +NS_MUC = "http://jabber.org/protocol/muc" +AFFILIATIONS = ("owner", "admin", "member", "none", "outcast") +ROOM_USER_JOINED = "ROOM_USER_JOINED" +ROOM_USER_LEFT = "ROOM_USER_LEFT" +OCCUPANT_KEYS = ("nick", "entity", "affiliation", "role") ROOM_STATE_OCCUPANTS = "occupants" ROOM_STATE_SELF_PRESENCE = "self-presence" ROOM_STATE_LIVE = "live" @@ -69,9 +69,9 @@ HISTORY_MAM = "mam" -CONFIG_SECTION = 'plugin muc' +CONFIG_SECTION = "plugin muc" -default_conf = {"default_muc": 'sat@chat.jabberfr.org'} +default_conf = {"default_muc": "sat@chat.jabberfr.org"} class AlreadyJoined(exceptions.ConflictError): @@ -92,58 +92,101 @@ # return same arguments as muc_room_joined + a boolean set to True is the room was # already joined (first argument) host.bridge.add_method( - "muc_join", ".plugin", in_sign='ssa{ss}s', out_sign='(bsa{sa{ss}}ssass)', - method=self._join, async_=True) + "muc_join", + ".plugin", + in_sign="ssa{ss}s", + out_sign="(bsa{sa{ss}}ssass)", + method=self._join, + async_=True, + ) host.bridge.add_method( - "muc_nick", ".plugin", in_sign='sss', out_sign='', method=self._nick) + "muc_nick", ".plugin", in_sign="sss", out_sign="", method=self._nick + ) host.bridge.add_method( - "muc_nick_get", ".plugin", in_sign='ss', out_sign='s', method=self._get_room_nick) + "muc_nick_get", + ".plugin", + in_sign="ss", + out_sign="s", + method=self._get_room_nick, + ) host.bridge.add_method( - "muc_leave", ".plugin", in_sign='ss', out_sign='', method=self._leave, - async_=True) + "muc_leave", + ".plugin", + in_sign="ss", + out_sign="", + method=self._leave, + async_=True, + ) host.bridge.add_method( - "muc_occupants_get", ".plugin", in_sign='ss', out_sign='a{sa{ss}}', - method=self._get_room_occupants) + "muc_occupants_get", + ".plugin", + in_sign="ss", + out_sign="a{sa{ss}}", + method=self._get_room_occupants, + ) host.bridge.add_method( - "muc_subject", ".plugin", in_sign='sss', out_sign='', method=self._subject) + "muc_subject", ".plugin", in_sign="sss", out_sign="", method=self._subject + ) host.bridge.add_method( - "muc_get_rooms_joined", ".plugin", in_sign='s', out_sign='a(sa{sa{ss}}ssas)', - method=self._get_rooms_joined) + "muc_get_rooms_joined", + ".plugin", + in_sign="s", + out_sign="a(sa{sa{ss}}ssas)", + method=self._get_rooms_joined, + ) host.bridge.add_method( - "muc_get_unique_room_name", ".plugin", in_sign='ss', out_sign='s', - method=self._get_unique_name) + "muc_get_unique_room_name", + ".plugin", + in_sign="ss", + out_sign="s", + method=self._get_unique_name, + ) host.bridge.add_method( - "muc_configure_room", ".plugin", in_sign='ss', out_sign='s', - method=self._configure_room, async_=True) + "muc_configure_room", + ".plugin", + in_sign="ss", + out_sign="s", + method=self._configure_room, + async_=True, + ) host.bridge.add_method( - "muc_get_default_service", ".plugin", in_sign='', out_sign='s', - method=self.get_default_muc) + "muc_get_default_service", + ".plugin", + in_sign="", + out_sign="s", + method=self.get_default_muc, + ) host.bridge.add_method( - "muc_get_service", ".plugin", in_sign='ss', out_sign='s', - method=self._get_muc_service, async_=True) + "muc_get_service", + ".plugin", + in_sign="ss", + out_sign="s", + method=self._get_muc_service, + 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.add_signal( - "muc_room_prepare_join", ".plugin", signature='ss') + host.bridge.add_signal("muc_room_prepare_join", ".plugin", signature="ss") # args: room_jid, occupants, user_nick, subject, profile - host.bridge.add_signal( - "muc_room_joined", ".plugin", signature='sa{sa{ss}}ssass') + host.bridge.add_signal("muc_room_joined", ".plugin", signature="sa{sa{ss}}ssass") # args: room_jid, profile - host.bridge.add_signal( - "muc_room_left", ".plugin", signature='ss') + host.bridge.add_signal("muc_room_left", ".plugin", signature="ss") # args: room_jid, old_nick, new_nick, profile - host.bridge.add_signal( - "muc_room_user_changed_nick", ".plugin", signature='ssss') + host.bridge.add_signal("muc_room_user_changed_nick", ".plugin", signature="ssss") # args: room_jid, subject, profile - host.bridge.add_signal( - "muc_room_new_subject", ".plugin", signature='sss') + host.bridge.add_signal("muc_room_new_subject", ".plugin", signature="sss") self.__submit_conf_id = host.register_callback( - self._submit_configuration, with_data=True) + self._submit_configuration, with_data=True + ) self._room_join_id = host.register_callback(self._ui_room_join_cb, with_data=True) host.import_menu( - (D_("MUC"), D_("configure")), self._configure_room_menu, security_limit=0, - help_string=D_("Configure Multi-User Chat room"), type_=C.MENU_ROOM) + (D_("MUC"), D_("configure")), + self._configure_room_menu, + 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: @@ -157,7 +200,9 @@ host.trigger.add("presence_available", self.presence_trigger) host.trigger.add("presence_received", self.presence_received_trigger) - host.trigger.add("message_received", self.message_received_trigger, priority=1000000) + host.trigger.add( + "message_received", self.message_received_trigger, priority=1000000 + ) host.trigger.add("message_parse", self._message_parse_trigger) async def profile_connected(self, client): @@ -179,7 +224,7 @@ if message_elt.getAttribute("type") == C.MESS_TYPE_GROUPCHAT: if message_elt.subject: return False - from_jid = jid.JID(message_elt['from']) + from_jid = jid.JID(message_elt["from"]) room_jid = from_jid.userhostJID() if room_jid in client._muc_client.joined_rooms: room = client._muc_client.joined_rooms[room_jid] @@ -189,16 +234,19 @@ # messages before history is complete, so this is not a warning # but an expected case. # On the other hand, with legacy history, it's not normal. - log.warning(_( - "Received non delayed message in a room before its " - "initialisation: state={state}, msg={msg}").format( - state=room.state, - msg=message_elt.toXml())) + log.warning( + _( + "Received non delayed message in a room before its " + "initialisation: state={state}, msg={msg}" + ).format(state=room.state, msg=message_elt.toXml()) + ) room._cache.append(message_elt) return False else: - log.warning("Received groupchat message for a room which has not been " - "joined, ignoring it: {}".format(message_elt.toXml())) + log.warning( + "Received groupchat message for a room which has not been " + "joined, ignoring it: {}".format(message_elt.toXml()) + ) return False return True @@ -284,34 +332,44 @@ # plugin should be refactored. getattr(room, "subject", ""), [s.name for s in room.statuses], - profile - ] + profile, + ] def _ui_room_join_cb(self, data, profile): - room_jid = jid.JID(data['index']) + room_jid = jid.JID(data["index"]) client = self.host.get_client(profile) - defer.ensureDeferred( - self.join(client, room_jid) - ) + defer.ensureDeferred(self.join(client, room_jid)) return {} def _password_ui_cb(self, data, client, room_jid, nick): """Called when the user has given room password (or cancelled)""" if C.bool(data.get(C.XMLUI_DATA_CANCELLED, "false")): log.info("room join for {} is cancelled".format(room_jid.userhost())) - raise failure.Failure(exceptions.CancelError(D_("Room joining cancelled by user"))) - password = data[xml_tools.form_escape('password')] - return client._muc_client.join(room_jid, nick, password).addCallbacks(self._join_cb, self._join_eb, (client, room_jid, nick), errbackArgs=(client, room_jid, nick, password)) + raise failure.Failure( + exceptions.CancelError(D_("Room joining cancelled by user")) + ) + password = data[xml_tools.form_escape("password")] + return client._muc_client.join(room_jid, nick, password).addCallbacks( + self._join_cb, + self._join_eb, + (client, room_jid, nick), + errbackArgs=(client, room_jid, nick, password), + ) def _show_list_ui(self, items, client, service): - xmlui = xml_tools.XMLUI(title=D_('Rooms in {}'.format(service.full()))) - adv_list = xmlui.change_container('advanced_list', columns=1, selectable='single', callback_id=self._room_join_id) + xmlui = xml_tools.XMLUI(title=D_("Rooms in {}".format(service.full()))) + adv_list = xmlui.change_container( + "advanced_list", + columns=1, + selectable="single", + callback_id=self._room_join_id, + ) items = sorted(items, key=lambda i: i.name.lower()) for item in items: adv_list.set_row_index(item.entity.full()) xmlui.addText(item.name) adv_list.end() - self.host.action_new({'xmlui': xmlui.toXml()}, profile=client.profile) + self.host.action_new({"xmlui": xmlui.toXml()}, profile=client.profile) def _join_cb(self, room, client, room_jid, nick): """Called when the user is in the requested room""" @@ -321,8 +379,10 @@ # a proper configuration management should be done log.debug(_("room locked !")) d = client._muc_client.configure(room.roomJID, {}) - d.addErrback(self.host.log_errback, - msg=_('Error while configuring the room: {failure_}')) + d.addErrback( + self.host.log_errback, + msg=_("Error while configuring the room: {failure_}"), + ) return room.fully_joined def _join_eb(self, failure_, client, room_jid, nick, password): @@ -330,33 +390,50 @@ try: condition = failure_.value.condition except AttributeError: - msg_suffix = f': {failure_}' + msg_suffix = f": {failure_}" else: - if condition == 'conflict': + if condition == "conflict": # we have a nickname conflict, we try again with "_" suffixed to current nickname - nick += '_' - return client._muc_client.join(room_jid, nick, password).addCallbacks(self._join_cb, self._join_eb, (client, room_jid, nick), errbackArgs=(client, room_jid, nick, password)) - elif condition == 'not-allowed': + nick += "_" + return client._muc_client.join(room_jid, nick, password).addCallbacks( + self._join_cb, + self._join_eb, + (client, room_jid, nick), + errbackArgs=(client, room_jid, nick, password), + ) + elif condition == "not-allowed": # room is restricted, we need a password - password_ui = xml_tools.XMLUI("form", title=D_('Room {} is restricted').format(room_jid.userhost()), submit_id='') - password_ui.addText(D_("This room is restricted, please enter the password")) - password_ui.addPassword('password') + password_ui = xml_tools.XMLUI( + "form", + title=D_("Room {} is restricted").format(room_jid.userhost()), + submit_id="", + ) + password_ui.addText( + D_("This room is restricted, please enter the password") + ) + password_ui.addPassword("password") d = xml_tools.defer_xmlui(self.host, password_ui, profile=client.profile) d.addCallback(self._password_ui_cb, client, room_jid, nick) return d msg_suffix = ' with condition "{}"'.format(failure_.value.condition) - mess = D_("Error while joining the room {room}{suffix}".format( - room = room_jid.userhost(), suffix = msg_suffix)) + mess = D_( + "Error while joining the room {room}{suffix}".format( + room=room_jid.userhost(), suffix=msg_suffix + ) + ) log.warning(mess) xmlui = xml_tools.note(mess, D_("Group chat error"), level=C.XMLUI_DATA_LVL_ERROR) - self.host.action_new({'xmlui': xmlui.toXml()}, profile=client.profile) + self.host.action_new({"xmlui": xmlui.toXml()}, profile=client.profile) @staticmethod def _get_occupants(room): """Get occupants of a room in a form suitable for bridge""" - return {u.nick: {k:str(getattr(u,k) or '') for k in OCCUPANT_KEYS} for u in list(room.roster.values())} + return { + u.nick: {k: str(getattr(u, k) or "") for k in OCCUPANT_KEYS} + for u in list(room.roster.values()) + } def _get_room_occupants(self, room_jid_s, profile_key): client = self.host.get_client(profile_key) @@ -377,11 +454,12 @@ for room in list(client._muc_client.joined_rooms.values()): if room.state == ROOM_STATE_LIVE: result.append( - (room.roomJID.userhost(), - self._get_occupants(room), - room.nick, - room.subject, - [s.name for s in room.statuses], + ( + room.roomJID.userhost(), + self._get_occupants(room), + room.nick, + room.subject, + [s.name for s in room.statuses], ) ) return result @@ -415,7 +493,7 @@ """ client = self.host.get_client(profile) try: - room_jid = jid.JID(menu_data['room_jid']) + room_jid = jid.JID(menu_data["room_jid"]) except KeyError: log.error(_("room_jid key is not present !")) return defer.fail(exceptions.DataError) @@ -425,6 +503,7 @@ msg = D_("No configuration available for this room") return {"xmlui": xml_tools.note(msg).toXml()} return {"xmlui": xmlui.toXml()} + return self.configure_room(client, room_jid).addCallback(xmlui_received) def configure_room(self, client, room_jid): @@ -457,15 +536,15 @@ session_data = self._sessions.profile_get(raw_data["session_id"], profile) except KeyError: log.warning(D_("Session ID doesn't exist, session has probably expired.")) - _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration failed')) + _dialog = xml_tools.XMLUI("popup", title=D_("Room configuration failed")) _dialog.addText(D_("Session ID doesn't exist, session has probably expired.")) - return defer.succeed({'xmlui': _dialog.toXml()}) + return defer.succeed({"xmlui": _dialog.toXml()}) data = xml_tools.xmlui_result_2_data_form_result(raw_data) - d = client._muc_client.configure(session_data['room_jid'], data) - _dialog = xml_tools.XMLUI('popup', title=D_('Room configuration succeed')) + d = client._muc_client.configure(session_data["room_jid"], data) + _dialog = xml_tools.XMLUI("popup", title=D_("Room configuration succeed")) _dialog.addText(D_("The new settings have been saved.")) - d.addCallback(lambda ignore: {'xmlui': _dialog.toXml()}) + d.addCallback(lambda ignore: {"xmlui": _dialog.toXml()}) del self._sessions[raw_data["session_id"]] return d @@ -477,13 +556,14 @@ def _get_muc_service(self, jid_=None, profile=C.PROF_KEY_NONE): client = self.host.get_client(profile) d = defer.ensureDeferred(self.get_muc_service(client, jid_ or None)) - d.addCallback(lambda service_jid: service_jid.full() if service_jid is not None else '') + d.addCallback( + lambda service_jid: service_jid.full() if service_jid is not None else "" + ) return d async def get_muc_service( - self, - client: SatXMPPEntity, - jid_: Optional[jid.JID] = None) -> Optional[jid.JID]: + self, client: SatXMPPEntity, jid_: Optional[jid.JID] = None + ) -> Optional[jid.JID]: """Return first found MUC service of an entity @param jid_: entity which may have a MUC service, or None for our own server @@ -497,7 +577,9 @@ else: # we have a cached value, we return it return muc_service - services = await self.host.find_service_entities(client, "conference", "text", jid_) + services = await self.host.find_service_entities( + client, "conference", "text", jid_ + ) for service in services: if ".irc." not in service.userhost(): # FIXME: @@ -514,10 +596,7 @@ return self.get_unique_name(client, muc_service or None).full() def get_unique_name( - self, - client: SatXMPPEntity, - muc_service: jid.JID|None = None, - prefix: str = "" + self, client: SatXMPPEntity, muc_service: jid.JID | None = None, prefix: str = "" ) -> jid.JID: """Return unique name for a room, avoiding collision @@ -546,7 +625,9 @@ @return: unicode """ - return self.host.memory.config_get(CONFIG_SECTION, 'default_muc', default_conf['default_muc']) + return self.host.memory.config_get( + CONFIG_SECTION, "default_muc", default_conf["default_muc"] + ) def _bridge_join_eb(self, failure_, client): failure_.trap(AlreadyJoined) @@ -564,19 +645,23 @@ try: room_jid = jid.JID(room_jid_s) except (RuntimeError, jid.InvalidFormat, AttributeError): - return defer.fail(jid.InvalidFormat(_("Invalid room identifier: {room_id}'. Please give a room short or full identifier like 'room' or 'room@{muc_service}'.").format( - room_id=room_jid_s, - muc_service=str(muc_service)))) + return defer.fail( + jid.InvalidFormat( + _( + "Invalid room identifier: {room_id}'. Please give a room short or full identifier like 'room' or 'room@{muc_service}'." + ).format(room_id=room_jid_s, muc_service=str(muc_service)) + ) + ) if not room_jid.user: room_jid.user, room_jid.host = room_jid.host, muc_service else: room_jid = self.get_unique_name(client) # TODO: error management + signal in bridge - d = defer.ensureDeferred( - self.join(client, room_jid, nick, options or None) + d = defer.ensureDeferred(self.join(client, room_jid, nick, options or None)) + + d.addCallback( + lambda room: [False] + self._get_room_joined_args(room, client.profile) ) - - d.addCallback(lambda room: [False] + self._get_room_joined_args(room, client.profile)) d.addErrback(self._bridge_join_eb, client) return d @@ -585,7 +670,7 @@ client: SatXMPPEntity, room_jid: jid.JID, nick: Optional[str] = None, - options: Optional[dict] = None + options: Optional[dict] = None, ) -> Optional[muc.Room]: if not nick: nick = client.jid.user @@ -593,11 +678,17 @@ options = {} if room_jid in client._muc_client.joined_rooms: room = client._muc_client.joined_rooms[room_jid] - log.info(_('{profile} is already in room {room_jid}').format( - profile=client.profile, room_jid = room_jid.userhost())) + log.info( + _("{profile} is already in room {room_jid}").format( + profile=client.profile, room_jid=room_jid.userhost() + ) + ) raise AlreadyJoined(room) - log.info(_("[{profile}] is joining room {room} with nick {nick}").format( - profile=client.profile, room=room_jid.userhost(), nick=nick)) + log.info( + _("[{profile}] is joining room {room} with nick {nick}").format( + profile=client.profile, room=room_jid.userhost(), nick=nick + ) + ) self.host.bridge.muc_room_prepare_join(room_jid.userhost(), client.profile) password = options.get("password") @@ -611,9 +702,7 @@ else: room.on_joined_callbacks = [] room.on_left_callbacks = [] - await defer.ensureDeferred( - self._join_cb(room, client, room_jid, nick) - ) + await defer.ensureDeferred(self._join_cb(room, client, room_jid, nick)) return room def pop_rooms(self, client): @@ -669,7 +758,7 @@ if options is None: options = {} self.check_room_joined(client, room_jid) - return client._muc_client.kick(room_jid, nick, reason=options.get('reason', None)) + return client._muc_client.kick(room_jid, nick, reason=options.get("reason", None)) def ban(self, client, entity_jid, room_jid, options=None): """Ban an entity from the room @@ -683,7 +772,9 @@ options = {} assert not entity_jid.resource assert not room_jid.resource - return client._muc_client.ban(room_jid, entity_jid, reason=options.get('reason', None)) + return client._muc_client.ban( + room_jid, entity_jid, reason=options.get("reason", None) + ) def affiliate(self, client, entity_jid, room_jid, options): """Change the affiliation of an entity @@ -695,9 +786,11 @@ self.check_room_joined(client, room_jid) assert not entity_jid.resource assert not room_jid.resource - assert 'affiliation' in options + assert "affiliation" in options # TODO: handles reason and nick - return client._muc_client.modifyAffiliationList(room_jid, [entity_jid], options['affiliation']) + return client._muc_client.modifyAffiliationList( + room_jid, [entity_jid], options["affiliation"] + ) # Text commands # @@ -733,9 +826,7 @@ muc_service = client.muc_service or "" nick = client.jid.user room_jid = self.text_cmds.get_room_jid(room_raw, muc_service) - defer.ensureDeferred( - self.join(client, room_jid, nick, {}) - ) + defer.ensureDeferred(self.join(client, room_jid, nick, {})) return False @@ -778,18 +869,19 @@ self.text_cmds.feed_back(client, feedback, mess_data) return False - reason = ' '.join(options[1:]) if len(options) > 1 else None + reason = " ".join(options[1:]) if len(options) > 1 else None d = self.kick(client, nick, mess_data["to"], {"reason": reason}) def cb(__): - feedback_msg = _('You have kicked {}').format(nick) + feedback_msg = _("You have kicked {}").format(nick) if reason is not None: - feedback_msg += _(' for the following reason: {reason}').format( + feedback_msg += _(" for the following reason: {reason}").format( reason=reason ) self.text_cmds.feed_back(client, feedback_msg, mess_data) return True + d.addCallback(cb) return d @@ -804,28 +896,34 @@ try: jid_s = options[0] entity_jid = jid.JID(jid_s).userhostJID() - assert(entity_jid.user) - assert(entity_jid.host) - except (RuntimeError, jid.InvalidFormat, AttributeError, IndexError, - AssertionError): + assert entity_jid.user + assert entity_jid.host + except ( + RuntimeError, + jid.InvalidFormat, + AttributeError, + IndexError, + AssertionError, + ): feedback = _( "You must provide a valid JID to ban, like in '/ban contact@example.net'" ) self.text_cmds.feed_back(client, feedback, mess_data) return False - reason = ' '.join(options[1:]) if len(options) > 1 else None + reason = " ".join(options[1:]) if len(options) > 1 else None d = self.ban(client, entity_jid, mess_data["to"], {"reason": reason}) def cb(__): - feedback_msg = _('You have banned {}').format(entity_jid) + feedback_msg = _("You have banned {}").format(entity_jid) if reason is not None: - feedback_msg += _(' for the following reason: {reason}').format( + feedback_msg += _(" for the following reason: {reason}").format( reason=reason ) self.text_cmds.feed_back(client, feedback_msg, mess_data) return True + d.addCallback(cb) return d @@ -844,26 +942,40 @@ try: jid_s = options[0] entity_jid = jid.JID(jid_s).userhostJID() - assert(entity_jid.user) - assert(entity_jid.host) - except (RuntimeError, jid.InvalidFormat, AttributeError, IndexError, AssertionError): - feedback = _("You must provide a valid JID to affiliate, like in '/affiliate contact@example.net member'") + assert entity_jid.user + assert entity_jid.host + except ( + RuntimeError, + jid.InvalidFormat, + AttributeError, + IndexError, + AssertionError, + ): + feedback = _( + "You must provide a valid JID to affiliate, like in '/affiliate contact@example.net member'" + ) self.text_cmds.feed_back(client, feedback, mess_data) return False - affiliation = options[1] if len(options) > 1 else 'none' + affiliation = options[1] if len(options) > 1 else "none" if affiliation not in AFFILIATIONS: - feedback = _("You must provide a valid affiliation: %s") % ' '.join(AFFILIATIONS) + feedback = _("You must provide a valid affiliation: %s") % " ".join( + AFFILIATIONS + ) self.text_cmds.feed_back(client, feedback, mess_data) return False - d = self.affiliate(client, entity_jid, mess_data["to"], {'affiliation': affiliation}) + d = self.affiliate( + client, entity_jid, mess_data["to"], {"affiliation": affiliation} + ) def cb(__): - feedback_msg = _('New affiliation for {entity}: {affiliation}').format( - entity=entity_jid, affiliation=affiliation) + feedback_msg = _("New affiliation for {entity}: {affiliation}").format( + entity=entity_jid, affiliation=affiliation + ) self.text_cmds.feed_back(client, feedback_msg, mess_data) return True + d.addCallback(cb) return d @@ -901,14 +1013,15 @@ try: service = jid.JID(unparsed) except RuntimeError: - if mess_data['type'] == C.MESS_TYPE_GROUPCHAT: + if mess_data["type"] == C.MESS_TYPE_GROUPCHAT: room_jid = mess_data["to"] service = jid.JID(room_jid.host) elif client.muc_service is not None: service = client.muc_service else: msg = D_("No known default MUC service {unparsed}").format( - unparsed=unparsed) + unparsed=unparsed + ) self.text_cmds.feed_back(client, msg, mess_data) return False except jid.InvalidFormat: @@ -921,21 +1034,23 @@ return False def _whois(self, client, whois_msg, mess_data, target_jid): - """ Add MUC user information to whois """ - if mess_data['type'] != "groupchat": + """Add MUC user information to whois""" + if mess_data["type"] != "groupchat": return if target_jid.userhostJID() not in client._muc_client.joined_rooms: log.warning(_("This room has not been joined")) return if not target_jid.resource: return - user = client._muc_client.joined_rooms[target_jid.userhostJID()].getUser(target_jid.resource) + user = client._muc_client.joined_rooms[target_jid.userhostJID()].getUser( + target_jid.resource + ) whois_msg.append(_("Nickname: %s") % user.nick) if user.entity: whois_msg.append(_("Entity: %s") % user.entity) - if user.affiliation != 'none': + if user.affiliation != "none": whois_msg.append(_("Affiliation: %s") % user.affiliation) - if user.role != 'none': + if user.role != "none": whois_msg.append(_("Role: %s") % user.role) if user.status: whois_msg.append(_("Status: %s") % user.status) @@ -948,7 +1063,7 @@ muc_client = client._muc_client for room_jid, room in muc_client.joined_rooms.items(): elt = xml_tools.element_copy(presence_elt) - elt['to'] = room_jid.userhost() + '/' + room.nick + elt["to"] = room_jid.userhost() + "/" + room.nick client.presence.send(elt) return True @@ -968,7 +1083,7 @@ self.plugin_parent = plugin_parent muc.MUCClient.__init__(self) self._changing_nicks = set() # used to keep trace of who is changing nick, - # and to discard userJoinedRoom signal in this case + # and to discard userJoinedRoom signal in this case print("init SatMUCClient OK") @property @@ -1000,17 +1115,23 @@ if new_state_idx == -1: raise exceptions.InternalError("unknown room state") if new_state_idx < 1: - raise exceptions.InternalError("unexpected new room state ({room}): {state}".format( - room=room.userhost(), - state=new_state)) - expected_state = ROOM_STATES[new_state_idx-1] + raise exceptions.InternalError( + "unexpected new room state ({room}): {state}".format( + room=room.userhost(), state=new_state + ) + ) + expected_state = ROOM_STATES[new_state_idx - 1] if room.state != expected_state: - log.error(_( - "room {room} is not in expected state: room is in state {current_state} " - "while we were expecting {expected_state}").format( - room=room.roomJID.userhost(), - current_state=room.state, - expected_state=expected_state)) + log.error( + _( + "room {room} is not in expected state: room is in state {current_state} " + "while we were expecting {expected_state}" + ).format( + room=room.roomJID.userhost(), + current_state=room.state, + expected_state=expected_state, + ) + ) room.state = new_state def _addRoom(self, room): @@ -1026,11 +1147,7 @@ room._cache_presence = {} async def _join_legacy( - self, - client: SatXMPPEntity, - room_jid: jid.JID, - nick: str, - password: Optional[str] + self, client: SatXMPPEntity, room_jid: jid.JID, nick: str, password: Optional[str] ) -> muc.Room: """Join room an retrieve history with legacy method""" mess_data_list = await self.host.memory.history_get( @@ -1038,7 +1155,7 @@ client.jid.userhostJID(), limit=1, between=True, - profile=client.profile + profile=client.profile, ) if mess_data_list: timestamp = mess_data_list[0][1] @@ -1049,7 +1166,8 @@ seconds = None room = await super(LiberviaMUCClient, self).join( - room_jid, nick, muc.HistoryOptions(seconds=seconds), password) + room_jid, nick, muc.HistoryOptions(seconds=seconds), password + ) # used to send bridge signal once backlog are written in history room._history_type = HISTORY_LEGACY room._history_d = defer.Deferred() @@ -1057,10 +1175,7 @@ return room async def _get_mam_history( - self, - client: SatXMPPEntity, - room: muc.Room, - room_jid: jid.JID + self, client: SatXMPPEntity, room: muc.Room, room_jid: jid.JID ) -> None: """Retrieve history for rooms handling MAM""" history_d = room._history_d = defer.Deferred() @@ -1073,39 +1188,39 @@ None, limit=1, between=False, - filters={ - 'types': C.MESS_TYPE_GROUPCHAT, - 'last_stanza_id': True}, - profile=client.profile) + filters={"types": C.MESS_TYPE_GROUPCHAT, "last_stanza_id": True}, + profile=client.profile, + ) if last_mess: - stanza_id = last_mess[0][-1]['stanza_id'] + stanza_id = last_mess[0][-1]["stanza_id"] rsm_req = rsm.RSMRequest(max_=20, after=stanza_id) - no_loop=False + no_loop = False else: - log.info("We have no MAM archive for room {room_jid}.".format( - room_jid=room_jid)) + log.info( + "We have no MAM archive for room {room_jid}.".format(room_jid=room_jid) + ) # we don't want the whole archive if we have no archive yet # as it can be huge - rsm_req = rsm.RSMRequest(max_=50, before='') - no_loop=True + rsm_req = rsm.RSMRequest(max_=50, before="") + no_loop = True mam_req = mam.MAMRequest(rsm_=rsm_req) complete = False count = 0 while not complete: try: - mam_data = await self._mam.get_archives(client, mam_req, - service=room_jid) + mam_data = await self._mam.get_archives(client, mam_req, service=room_jid) except xmpp_error.StanzaError as e: - if last_mess and e.condition == 'item-not-found': + if last_mess and e.condition == "item-not-found": log.warning( f"requested item (with id {stanza_id!r}) can't be found in " f"history of {room_jid}, history has probably been purged on " - f"server.") + f"server." + ) # we get last items like for a new room - rsm_req = rsm.RSMRequest(max_=50, before='') + rsm_req = rsm.RSMRequest(max_=50, before="") mam_req = mam.MAMRequest(rsm_=rsm_req) - no_loop=True + no_loop = True continue else: raise e @@ -1122,47 +1237,56 @@ for mess_elt in elt_list: try: fwd_message_elt = self._mam.get_message_from_result( - client, mess_elt, mam_req, service=room_jid) + client, mess_elt, mam_req, service=room_jid + ) except exceptions.DataError: continue if fwd_message_elt.getAttribute("to"): log.warning( 'Forwarded message element has a "to" attribute while it is ' - 'forbidden by specifications') + "forbidden by specifications" + ) fwd_message_elt["to"] = client.jid.full() client.messageProt.onMessage(fwd_message_elt) client._muc_client._onGroupChat(fwd_message_elt) if not count: - log.info(_("No message received while offline in {room_jid}".format( - room_jid=room_jid))) + log.info( + _( + "No message received while offline in {room_jid}".format( + room_jid=room_jid + ) + ) + ) else: log.info( - _("We have received {num_mess} message(s) in {room_jid} while " - "offline.") - .format(num_mess=count, room_jid=room_jid)) + _( + "We have received {num_mess} message(s) in {room_jid} while " + "offline." + ).format(num_mess=count, room_jid=room_jid) + ) # for legacy history, the following steps are done in receivedSubject but for MAM # 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.change_room_state(room, ROOM_STATE_LIVE) - history_d.addCallbacks(self._history_cb, self._history_eb, [room], - errbackArgs=[room]) + history_d.addCallbacks( + self._history_cb, self._history_eb, [room], errbackArgs=[room] + ) # we wait for all callbacks to be processed await history_d async def _join_mam( - self, - client: SatXMPPEntity, - room_jid: jid.JID, - nick: str, - password: Optional[str] + self, client: SatXMPPEntity, room_jid: jid.JID, nick: str, password: Optional[str] ) -> muc.Room: """Join room and retrieve history using MAM""" room = await super(LiberviaMUCClient, self).join( # 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_jid, + nick, + muc.HistoryOptions(maxStanzas=0), + password=password, ) room._history_type = HISTORY_MAM # MAM history retrieval can be very long, and doesn't need to be sync, so we don't @@ -1197,8 +1321,11 @@ if user is None: nick = presence.sender.resource if not nick: - log.warning(_("missing nick in presence: {xml}").format( - xml = presence.toElement().toXml())) + log.warning( + _("missing nick in presence: {xml}").format( + xml=presence.toElement().toXml() + ) + ) return user = muc.User(nick, presence.entity) @@ -1207,8 +1334,10 @@ # like 110 (REALJID_PUBLIC) after first <presence/> received # so we keep only the initial <presence> (with SELF_PRESENCE), # thus we check if attribute already exists - if (not hasattr(room, 'statuses') - and muc.STATUS_CODE.SELF_PRESENCE in presence.mucStatuses): + if ( + not hasattr(room, "statuses") + and muc.STATUS_CODE.SELF_PRESENCE in presence.mucStatuses + ): room.statuses = presence.mucStatuses # Update user data @@ -1250,8 +1379,11 @@ # we have received our own nick, # this mean that the full room roster was received self.change_room_state(room, ROOM_STATE_SELF_PRESENCE) - log.debug("room {room} joined with nick {nick}".format( - room=room.occupantJID.userhost(), nick=user.nick)) + log.debug( + "room {room} joined with nick {nick}".format( + room=room.occupantJID.userhost(), nick=user.nick + ) + ) # we set type so we don't have to use a deferred # with disco to check entity type self.host.memory.update_entity_data( @@ -1262,9 +1394,9 @@ "Received user presence data in a room before its initialisation " "(current state: {state})," "this is not standard! Ignoring it: {room} ({nick})".format( - state=room.state, - room=room.roomJID.userhost(), - nick=user.nick)) + state=room.state, room=room.roomJID.userhost(), nick=user.nick + ) + ) return else: if not room.fully_joined.called: @@ -1275,24 +1407,29 @@ self._changing_nicks.remove(user.nick) except KeyError: # this is a new user - log.debug(_("user {nick} has joined room {room_id}").format( - nick=user.nick, room_id=room.occupantJID.userhost())) + log.debug( + _("user {nick} has joined room {room_id}").format( + nick=user.nick, room_id=room.occupantJID.userhost() + ) + ) if not self.host.trigger.point( - "MUC user joined", room, user, self.client.profile): + "MUC user joined", room, user, self.client.profile + ): return - extra = {'info_type': ROOM_USER_JOINED, - 'user_affiliation': user.affiliation, - 'user_role': user.role, - 'user_nick': user.nick - } + extra = { + "info_type": ROOM_USER_JOINED, + "user_affiliation": user.affiliation, + "user_role": user.role, + "user_nick": user.nick, + } if user.entity is not None: - extra['user_entity'] = user.entity.full() + extra["user_entity"] = user.entity.full() mess_data = { # dict is similar to the one used in client.onMessage "from": room.roomJID, "to": self.client.jid, "uid": str(uuid.uuid4()), - "message": {'': D_("=> {} has joined the room").format(user.nick)}, + "message": {"": D_("=> {} has joined the room").format(user.nick)}, "subject": {}, "type": C.MESS_TYPE_INFO, "extra": extra, @@ -1304,42 +1441,52 @@ # self.client.message_add_to_history(mess_data) self.client.message_send_to_bridge(mess_data) - def userLeftRoom(self, room, user): if not self.host.trigger.point("MUC user left", room, user, self.client.profile): return if user.nick == room.nick: # we left the room room_jid_s = room.roomJID.userhost() - log.info(_("Room ({room}) left ({profile})").format( - room = room_jid_s, profile = self.client.profile)) - self.host.memory.del_entity_cache(room.roomJID, profile_key=self.client.profile) + log.info( + _("Room ({room}) left ({profile})").format( + room=room_jid_s, profile=self.client.profile + ) + ) + self.host.memory.del_entity_cache( + room.roomJID, profile_key=self.client.profile + ) self.host.bridge.muc_room_left(room.roomJID.userhost(), self.client.profile) elif room.state != ROOM_STATE_LIVE: - log.warning("Received user presence data in a room before its initialisation (current state: {state})," + log.warning( + "Received user presence data in a room before its initialisation (current state: {state})," "this is not standard! Ignoring it: {room} ({nick})".format( - state=room.state, - room=room.roomJID.userhost(), - nick=user.nick)) + state=room.state, room=room.roomJID.userhost(), nick=user.nick + ) + ) return else: if not room.fully_joined.called: return - log.debug(_("user {nick} left room {room_id}").format(nick=user.nick, room_id=room.occupantJID.userhost())) + log.debug( + _("user {nick} left room {room_id}").format( + nick=user.nick, room_id=room.occupantJID.userhost() + ) + ) for cb in room.on_left_callbacks: defer.ensureDeferred(cb(room, user)) - extra = {'info_type': ROOM_USER_LEFT, - 'user_affiliation': user.affiliation, - 'user_role': user.role, - 'user_nick': user.nick - } + extra = { + "info_type": ROOM_USER_LEFT, + "user_affiliation": user.affiliation, + "user_role": user.role, + "user_nick": user.nick, + } if user.entity is not None: - extra['user_entity'] = user.entity.full() + extra["user_entity"] = user.entity.full() mess_data = { # dict is similar to the one used in client.onMessage "from": room.roomJID, "to": self.client.jid, "uid": str(uuid.uuid4()), - "message": {'': D_("<= {} has left the room").format(user.nick)}, + "message": {"": D_("<= {} has left the room").format(user.nick)}, "subject": {}, "type": C.MESS_TYPE_INFO, "extra": extra, @@ -1350,7 +1497,9 @@ self.client.message_send_to_bridge(mess_data) def user_changed_nick(self, room, user, new_nick): - self.host.bridge.muc_room_user_changed_nick(room.roomJID.userhost(), user.nick, new_nick, self.client.profile) + self.host.bridge.muc_room_user_changed_nick( + room.roomJID.userhost(), user.nick, new_nick, self.client.profile + ) def userUpdatedStatus(self, room, user, show, status): entity = jid.JID(tuple=(room.roomJID.user, room.roomJID.host, user.nick)) @@ -1365,16 +1514,20 @@ "user": user, "show": show, "status": status, - } + } return - statuses = {C.PRESENCE_STATUSES_DEFAULT: status or ''} + statuses = {C.PRESENCE_STATUSES_DEFAULT: status or ""} self.host.bridge.presence_update( - entity.full(), show or '', 0, statuses, self.client.profile) + entity.full(), show or "", 0, statuses, self.client.profile + ) ## messages ## def receivedGroupChat(self, room, user, body): - log.debug('receivedGroupChat: room=%s user=%s body=%s' % (room.roomJID.full(), user, body)) + log.debug( + "receivedGroupChat: room=%s user=%s body=%s" + % (room.roomJID.full(), user, body) + ) ## subject ## @@ -1390,8 +1543,11 @@ room, user = self._getRoomUser(message) if room is None: - log.warning("No room found for message: {message}" - .format(message=message.toElement().toXml())) + log.warning( + "No room found for message: {message}".format( + message=message.toElement().toXml() + ) + ) return if message.subject is not None: @@ -1424,7 +1580,7 @@ for elem in cache: self.client.xmlstream.dispatch(elem) for presence_data in cache_presence.values(): - if not presence_data['show'] and not presence_data['status']: + if not presence_data["show"] and not presence_data["status"]: # occupants are already sent in muc_room_joined, so if we don't have # extra information like show or statuses, we can discard the signal continue @@ -1442,18 +1598,26 @@ if room.state != ROOM_STATE_LIVE: if room._history_type == HISTORY_LEGACY: self.change_room_state(room, ROOM_STATE_LIVE) - room._history_d.addCallbacks(self._history_cb, self._history_eb, [room], errbackArgs=[room]) + room._history_d.addCallbacks( + self._history_cb, self._history_eb, [room], errbackArgs=[room] + ) else: # the subject has been changed - log.debug(_("New subject for room ({room_id}): {subject}").format(room_id = room.roomJID.full(), subject = subject)) - self.host.bridge.muc_room_new_subject(room.roomJID.userhost(), subject, self.client.profile) + log.debug( + _("New subject for room ({room_id}): {subject}").format( + room_id=room.roomJID.full(), subject=subject + ) + ) + self.host.bridge.muc_room_new_subject( + room.roomJID.userhost(), subject, self.client.profile + ) ## disco ## - def getDiscoInfo(self, requestor, target, nodeIdentifier=''): + def getDiscoInfo(self, requestor, target, nodeIdentifier=""): return [disco.DiscoFeature(NS_MUC)] - def getDiscoItems(self, requestor, target, nodeIdentifier=''): + def getDiscoItems(self, requestor, target, nodeIdentifier=""): # TODO: manage room queries ? Bad for privacy, must be disabled by default # see XEP-0045 ยง 6.7 return []