# HG changeset patch # User Goffi # Date 1702501225 -3600 # Node ID 7eda7cb8a15c7ffddd2336b00aa01a0e435f7d6f # Parent b1207332cea272ea1771e0a4ed8d48b2dcd04407 plugin XEP-0424: fix ID used and recipients diff -r b1207332cea2 -r 7eda7cb8a15c libervia/backend/plugins/plugin_xep_0424.py --- a/libervia/backend/plugins/plugin_xep_0424.py Wed Dec 13 22:00:25 2023 +0100 +++ b/libervia/backend/plugins/plugin_xep_0424.py Wed Dec 13 22:00:25 2023 +0100 @@ -43,6 +43,7 @@ C.PI_MODES: C.PLUG_MODE_BOTH, C.PI_PROTOCOLS: ["XEP-0424"], C.PI_DEPENDENCIES: ["XEP-0334", "XEP-0428"], + C.PI_RECOMMENDATIONS: ["XEP-0045"], C.PI_MAIN: "XEP_0424", C.PI_HANDLER: "yes", C.PI_DESCRIPTION: _("""Implementation Message Retraction"""), @@ -67,12 +68,12 @@ class XEP_0424: - def __init__(self, host): log.info(f"plugin {PLUGIN_INFO[C.PI_NAME]!r} initialization") self.host = host host.memory.update_params(PARAMS) self._h = host.plugins["XEP-0334"] + self._m = host.plugins.get("XEP-0045") host.register_namespace("message-retract", NS_MESSAGE_RETRACT) host.trigger.add("message_received", self._message_received_trigger, 100) host.bridge.add_method( @@ -89,66 +90,65 @@ def _retract(self, message_id: str, profile: str) -> None: client = self.host.get_client(profile) - return defer.ensureDeferred( - self.retract(client, message_id) - ) + return defer.ensureDeferred(self.retract(client, message_id)) - def retract_by_origin_id( - self, - client: SatXMPPEntity, - peer_jid: jid.JID, - origin_id: str + def send_retract( + self, client: SatXMPPEntity, peer_jid: jid.JID, retract_id: str, history: History ) -> None: """Send a message retraction using origin-id [retract] should be prefered: internal ID should be used as it is independant of XEPs changes. However, in some case messages may not be stored in database (notably for some components), and then this method can be used - @param origin_id: origin-id as specified in XEP-0359 + + @param retract_id: ID (origin or stanza according to XEP-0359) of message to + retract + @param history: history instance of the message to retract """ message_elt = domish.Element((None, "message")) message_elt["from"] = client.jid.full() message_elt["to"] = peer_jid.full() + message_elt["type"] = history.type retract_elt = message_elt.addElement((NS_MESSAGE_RETRACT, "retract")) - retract_elt["id"] = origin_id + retract_elt["id"] = retract_id self.host.plugins["XEP-0428"].add_fallback_elt( message_elt, "[A message retraction has been requested, but your client doesn't support " - "it]" + "it]", ) self._h.add_hint_elements(message_elt, [self._h.HINT_STORE]) client.send(message_elt) - async def retract_by_history( - self, - client: SatXMPPEntity, - history: History - ) -> None: + async def retract_by_history(self, client: SatXMPPEntity, history: History) -> None: """Send a message retraction using History instance This method is to use instead of [retract] when the history instance is already retrieved. Note that the instance must have messages and subjets loaded @param history: history instance of the message to retract """ - try: - origin_id = history.origin_id - except KeyError: - raise exceptions.FeatureNotFound( - f"message to retract doesn't have the necessary origin-id, the sending " - "client is probably not supporting message retraction." - ) + if history.type == C.MESS_TYPE_GROUPCHAT: + is_group_chat = True + peer_jid = jid.JID(history.source) + retract_id = history.stanza_id else: - if history.type == C.MESS_TYPE_GROUPCHAT: - is_group_chat = True + is_group_chat = False + peer_jid = jid.JID(history.dest) + if self._m is not None and self._m.is_joined_room(client, peer_jid): + # it's actually a private MUC message, we need the full JID. peer_jid = history.dest_jid - else: - is_group_chat = False - peer_jid = jid.JID(history.dest) - self.retract_by_origin_id(client, peer_jid, origin_id) - if not is_group_chat: - # retraction will happen when message will be received in the - # chat. - await self.retract_db_history(client, history) + retract_id = history.origin_id + + if not retract_id: + raise exceptions.FeatureNotFound( + "Message to retract doesn't have the necessary ID, the sending client is " + "probably not supporting message retraction." + ) + + self.send_retract(client, peer_jid, retract_id, history) + if not is_group_chat: + # retraction will happen when message will be received in the + # chat. + await self.retract_db_history(client, history) async def retract( self, @@ -165,8 +165,11 @@ if not message_id: raise ValueError("message_id can't be empty") history = await self.host.memory.storage.get( - client, History, History.uid, message_id, - joined_loads=[History.messages, History.subjects, History.thread] + client, + History, + History.uid, + message_id, + joined_loads=[History.messages, History.subjects, History.thread], ) if history is None: raise exceptions.NotFound( @@ -186,14 +189,14 @@ keep_history = await self.host.memory.param_get_a_async( NAME, CATEGORY, profile_key=client.profile ) - old_version: Dict[str, Any] = { - "timestamp": time.time() - } + old_version: Dict[str, Any] = {"timestamp": time.time()} if keep_history: - old_version.update({ - "messages": [m.serialise() for m in history.messages], - "subjects": [s.serialise() for s in history.subjects], - }) + old_version.update( + { + "messages": [m.serialise() for m in history.messages], + "subjects": [s.serialise() for s in history.subjects], + } + ) history.extra.setdefault("old_versions", []).append(old_version) @@ -219,7 +222,7 @@ self, client: SatXMPPEntity, message_elt: domish.Element, - post_treat: defer.Deferred + post_treat: defer.Deferred, ) -> bool: retract_elt = next(message_elt.elements(NS_MESSAGE_RETRACT, "retract"), None) if not retract_elt: @@ -230,17 +233,23 @@ else: col_id = History.origin_id history = await self.host.memory.storage.get( - client, History, col_id, retract_elt["id"], - joined_loads=[History.messages, History.subjects, History.thread] + client, + History, + col_id, + retract_elt["id"], + joined_loads=[History.messages, History.subjects, History.thread], ) except KeyError: log.warning(f"invalid retract element, missing id: {retract_elt.toXml()}") return False from_jid = jid.JID(message_elt["from"]) - if ( - history is not None - and history.source_jid.userhostJID() != from_jid.userhostJID() + if history is not None and ( + (history.type == C.MESS_TYPE_GROUPCHAT and history.source_jid != from_jid) + or ( + history.type != C.MESS_TYPE_GROUPCHAT + and history.source_jid.userhostJID() != from_jid.userhostJID() + ) ): log.warning( f"Received message retraction from {from_jid.full()}, but the message to " @@ -257,9 +266,7 @@ if history is None: # we check history after the trigger because we may be in a component which # doesn't store messages in database. - log.warning( - f"No message found with given origin-id: {message_elt.toXml()}" - ) + log.warning(f"No message found with given id: {message_elt.toXml()}") return False log.info(f"[{client.profile}] retracting message {history.uid!r}") await self.retract_db_history(client, history) @@ -268,7 +275,6 @@ @implementer(disco.IDisco) class XEP_0424_handler(xmlstream.XMPPHandler): - def getDiscoInfo(self, __, target, nodeIdentifier=""): return [disco.DiscoFeature(NS_MESSAGE_RETRACT)]