plugin XEP-0308: fix message edit for MUC private messages:

- Keep the destination resource when resending the correction (e.g. `room/nick`).
- Match the corrected message by id (fallback: last message from the sender).
- Ignore corrections that can't be matched, or whose content is already applied (e.g.
  own corrections redelivered by MAM).
This commit is contained in:
Goffi 2026-08-25 17:36:48 +02:00
commit 403b9e9d31

View file

@ -163,58 +163,73 @@ class XEP_0308:
replace_elt = next(message_elt.elements(NS_MESSAGE_CORRECT, "replace"), None)
if not replace_elt:
return True
try:
replace_id = replace_elt["id"].strip()
if not replace_id:
raise KeyError
except KeyError:
replace_id = (replace_elt.getAttribute("id") or "").strip()
if not replace_id:
log.warning(f"Invalid message correction: {message_elt.toXml()}")
else:
return False
# look for the corrected message by id (this also handles our own messages, e.g.
# corrections from another device)
message_type = message_elt.getAttribute("type", C.MESS_TYPE_NORMAL)
edited_history = await self.host.memory.storage.get_history_from_xmpp_id(
client, replace_id, message_type
)
if edited_history is None:
# Fallback: last message from the sender (history without usable id)
edited_history = await self.get_last_history(client, message_elt)
if edited_history is None:
log.warning(
f"No message found from {from_jid.full()}, can't correct "
f"anything: {message_elt.toXml()}"
f"No message found to correct ({replace_id}), ignoring "
f"correction: {message_elt.toXml()}"
)
return True
return False
check_id = edited_history.origin_id or edited_history.extra.get("message_id")
if check_id != replace_id:
log.warning(
"Can't apply correction: it doesn't reference the last one:\n"
f"{message_elt.toXml()}"
)
return True
# TODO: this only accept same full JID, if we do more than last message
# correction, we may want to accept different resource for non groupchat
# messages (in case of reconnection with resource change for instance).
if edited_history.source_jid != from_jid:
log.warning(
"Can't apply correction: correction doesn't come from same author "
f"{edited_history.source_jid.full()}:\n{message_elt.toXml()}"
)
return True
previous_message_data = edited_history.serialise()
message_data = await client.messageProt.parse_message(message_elt)
if not message_data["message"] and not message_data["subject"]:
log.warning(
"Message correction doesn't have body nor subject, we can't edit "
"anything"
)
return False
await self.update_history(
client,
edited_history,
message_data.get("received_timestamp") or message_data["timestamp"],
message_data["message"],
message_data["subject"],
message_data["extra"],
previous_message_data["message"],
previous_message_data["subject"],
previous_message_data.get("extra"),
# for groupchat, the correction must come from the same occupant, for other types,
# the resource may have changed (e.g. reconnection)
same_author = (
edited_history.source_jid == from_jid
if edited_history.type == C.MESS_TYPE_GROUPCHAT
else edited_history.source_jid.userhost() == from_jid.userhost()
)
if not same_author:
log.warning(
"Can't apply correction: correction doesn't come from same author "
f"{edited_history.source_jid.full()}:\n{message_elt.toXml()}"
)
return False
previous_message_data = edited_history.serialise()
message_data = await client.messageProt.parse_message(message_elt)
if not message_data["message"] and not message_data["subject"]:
log.warning(
"Message correction doesn't have body nor subject, we can't edit anything"
)
return False
if (
message_data["message"] == previous_message_data["message"]
and message_data["subject"] == previous_message_data["subject"]
):
# content is already up to date (e.g. our own correction redelivered by MAM
# after reconnection)
log.debug(f"Ignoring already applied correction: {message_elt.toXml()}")
return False
await self.update_history(
client,
edited_history,
message_data.get("received_timestamp") or message_data["timestamp"],
message_data["message"],
message_data["subject"],
message_data["extra"],
previous_message_data["message"],
previous_message_data["subject"],
previous_message_data.get("extra"),
)
return False
async def message_edit(
@ -248,7 +263,8 @@ class XEP_0308:
peer_jid = edited_history.source_jid
else:
is_group_chat = False
peer_jid = jid.JID(edited_history.dest)
# keep the destination resource (e.g. room/nick for a MUC private message)
peer_jid = edited_history.dest_jid
history_data = await self.host.memory.history_get(
client.jid, peer_jid, limit=1, profile=client.profile
)