plugin XEP-0424: message retraction update:
- follow specification update (with namespace bump) - retract from history on message reception for group chat - send bridge message
This commit is contained in:
parent
ee4caf8ee7
commit
3c35dea8b1
6 changed files with 89 additions and 62 deletions
|
|
@ -126,6 +126,7 @@ class Const(object):
|
|||
|
||||
MESS_UPDATE_EDIT = "EDIT"
|
||||
MESS_UPDATE_REACTION = "REACTION"
|
||||
MESS_UPDATE_RETRACT = "RETRACT"
|
||||
|
||||
MESS_EXTRA_INFO = "info_type"
|
||||
EXTRA_INFO_DECR_ERR = "DECRYPTION_ERROR"
|
||||
|
|
|
|||
|
|
@ -32,11 +32,6 @@ class MessageUpdateData(BaseModel):
|
|||
super().__init__(**kwargs)
|
||||
|
||||
|
||||
class MessageEditData(MessageData):
|
||||
"""Data used when a new message edition has been received"""
|
||||
pass
|
||||
|
||||
|
||||
class MessageReactionData(MessageUpdateData):
|
||||
reactions: dict[str, list[str]] = Field(
|
||||
description="Reaction to reacting entities mapping"
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ class APGateway:
|
|||
self.ad_hoc = APAdHocService(self)
|
||||
self.ap_events = APEvents(self)
|
||||
host.trigger.add("message_received", self._message_received_trigger, priority=-1000)
|
||||
host.trigger.add("XEP-0424_retractReceived", self._on_message_retract)
|
||||
host.trigger.add("XEP-0424_retract_received", self._on_message_retract)
|
||||
host.trigger.add("XEP-0372_ref_received", self._on_reference_received)
|
||||
|
||||
host.bridge.add_method(
|
||||
|
|
@ -2321,7 +2321,7 @@ class APGateway:
|
|||
client: SatXMPPEntity,
|
||||
message_elt: domish.Element,
|
||||
retract_elt: domish.Element,
|
||||
fastened_elts
|
||||
history: History
|
||||
) -> bool:
|
||||
if client != self.client:
|
||||
return True
|
||||
|
|
@ -2341,7 +2341,7 @@ class APGateway:
|
|||
actor_id = await self.get_ap_actor_id_from_account(ap_account)
|
||||
inbox = await self.get_ap_inbox_from_id(actor_id, use_shared=False)
|
||||
url_actor, ap_item = await self.ap_delete_item(
|
||||
from_jid.userhostJID(), None, fastened_elts.id, public=False
|
||||
from_jid.userhostJID(), None, retract_elt["id"], public=False
|
||||
)
|
||||
resp = await self.sign_and_post(inbox, url_actor, ap_item)
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ from libervia.backend.core.core_types import SatXMPPEntity
|
|||
from libervia.backend.core.i18n import _
|
||||
from libervia.backend.core.log import getLogger
|
||||
from libervia.backend.memory.sqla import History, Message, Subject, joinedload, select
|
||||
from libervia.backend.models.core import MessageEditData, MessageEdition
|
||||
from libervia.backend.models.core import MessageData, MessageEdition
|
||||
from libervia.backend.tools.common import data_format
|
||||
from libervia.backend.tools.utils import aio
|
||||
log = getLogger(__name__)
|
||||
|
|
@ -140,7 +140,7 @@ class XEP_0308:
|
|||
edited_history.extra.setdefault(C.MESS_EXTRA_EDITIONS, []).append(previous_version)
|
||||
await self.host.memory.storage.add(edited_history)
|
||||
|
||||
edit_data = MessageEditData(edited_history.serialise())
|
||||
edit_data = MessageData(edited_history.serialise())
|
||||
self.host.bridge.message_update(
|
||||
edited_history.uid,
|
||||
C.MESS_UPDATE_EDIT,
|
||||
|
|
|
|||
|
|
@ -15,22 +15,23 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Dict, Any
|
||||
import time
|
||||
from copy import deepcopy
|
||||
from typing import Any, Dict
|
||||
|
||||
from twisted.words.protocols.jabber import xmlstream, jid
|
||||
from twisted.words.xish import domish
|
||||
from sqlalchemy.orm.attributes import flag_modified
|
||||
from twisted.internet import defer
|
||||
from twisted.words.protocols.jabber import jid, xmlstream
|
||||
from twisted.words.xish import domish
|
||||
from wokkel import disco
|
||||
from zope.interface import implementer
|
||||
|
||||
from libervia.backend.core.constants import Const as C
|
||||
from libervia.backend.core.i18n import _, D_
|
||||
from libervia.backend.core import exceptions
|
||||
from libervia.backend.core.core_types import SatXMPPEntity
|
||||
from libervia.backend.core.constants import Const as C
|
||||
from libervia.backend.core.core_types import MessageData, SatXMPPEntity
|
||||
from libervia.backend.core.i18n import D_, _
|
||||
from libervia.backend.core.log import getLogger
|
||||
from libervia.backend.memory.sqla_mapping import History
|
||||
from libervia.backend.tools.common import data_format
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
|
@ -40,14 +41,14 @@ PLUGIN_INFO = {
|
|||
C.PI_IMPORT_NAME: "XEP-0424",
|
||||
C.PI_TYPE: "XEP",
|
||||
C.PI_MODES: C.PLUG_MODE_BOTH,
|
||||
C.PI_PROTOCOLS: ["XEP-0334", "XEP-0424", "XEP-0428"],
|
||||
C.PI_DEPENDENCIES: ["XEP-0422"],
|
||||
C.PI_PROTOCOLS: ["XEP-0424"],
|
||||
C.PI_DEPENDENCIES: ["XEP-0334", "XEP-0428"],
|
||||
C.PI_MAIN: "XEP_0424",
|
||||
C.PI_HANDLER: "yes",
|
||||
C.PI_DESCRIPTION: _("""Implementation Message Retraction"""),
|
||||
}
|
||||
|
||||
NS_MESSAGE_RETRACT = "urn:xmpp:message-retract:0"
|
||||
NS_MESSAGE_RETRACT = "urn:xmpp:message-retract:1"
|
||||
|
||||
CATEGORY = "Privacy"
|
||||
NAME = "retract_history"
|
||||
|
|
@ -65,14 +66,13 @@ PARAMS = """
|
|||
)
|
||||
|
||||
|
||||
class XEP_0424(object):
|
||||
class XEP_0424:
|
||||
|
||||
def __init__(self, host):
|
||||
log.info(_("XEP-0424 (Message Retraction) plugin initialization"))
|
||||
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._f = host.plugins["XEP-0422"]
|
||||
host.register_namespace("message-retract", NS_MESSAGE_RETRACT)
|
||||
host.trigger.add("message_received", self._message_received_trigger, 100)
|
||||
host.bridge.add_method(
|
||||
|
|
@ -96,7 +96,7 @@ class XEP_0424(object):
|
|||
def retract_by_origin_id(
|
||||
self,
|
||||
client: SatXMPPEntity,
|
||||
dest_jid: jid.JID,
|
||||
peer_jid: jid.JID,
|
||||
origin_id: str
|
||||
) -> None:
|
||||
"""Send a message retraction using origin-id
|
||||
|
|
@ -108,9 +108,9 @@ class XEP_0424(object):
|
|||
"""
|
||||
message_elt = domish.Element((None, "message"))
|
||||
message_elt["from"] = client.jid.full()
|
||||
message_elt["to"] = dest_jid.full()
|
||||
apply_to_elt = self._f.apply_to_elt(message_elt, origin_id)
|
||||
apply_to_elt.addElement((NS_MESSAGE_RETRACT, "retract"))
|
||||
message_elt["to"] = peer_jid.full()
|
||||
retract_elt = message_elt.addElement((NS_MESSAGE_RETRACT, "retract"))
|
||||
retract_elt["id"] = origin_id
|
||||
self.host.plugins["XEP-0428"].add_fallback_elt(
|
||||
message_elt,
|
||||
"[A message retraction has been requested, but your client doesn't support "
|
||||
|
|
@ -138,8 +138,17 @@ class XEP_0424(object):
|
|||
"client is probably not supporting message retraction."
|
||||
)
|
||||
else:
|
||||
self.retract_by_origin_id(client, history.dest_jid, origin_id)
|
||||
await self.retract_db_history(client, history)
|
||||
if history.type == C.MESS_TYPE_GROUPCHAT:
|
||||
is_group_chat = True
|
||||
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 <retract> message will be received in the
|
||||
# chat.
|
||||
await self.retract_db_history(client, history)
|
||||
|
||||
async def retract(
|
||||
self,
|
||||
|
|
@ -157,7 +166,7 @@ class XEP_0424(object):
|
|||
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]
|
||||
joined_loads=[History.messages, History.subjects, History.thread]
|
||||
)
|
||||
if history is None:
|
||||
raise exceptions.NotFound(
|
||||
|
|
@ -171,12 +180,10 @@ class XEP_0424(object):
|
|||
@param history: history instance
|
||||
"messages" and "subjects" must be loaded too
|
||||
"""
|
||||
# FIXME: should be keep history? This is useful to check why a message has been
|
||||
# FIXME: should we keep history? This is useful to check why a message has been
|
||||
# retracted, but if may be bad if the user think it's really deleted
|
||||
# we assign a new object to be sure to trigger an update
|
||||
history.extra = deepcopy(history.extra) if history.extra else {}
|
||||
history.extra["retracted"] = True
|
||||
keep_history = self.host.memory.param_get_a(
|
||||
flag_modified(history, "extra")
|
||||
keep_history = await self.host.memory.param_get_a_async(
|
||||
NAME, CATEGORY, profile_key=client.profile
|
||||
)
|
||||
old_version: Dict[str, Any] = {
|
||||
|
|
@ -185,13 +192,27 @@ class XEP_0424(object):
|
|||
if keep_history:
|
||||
old_version.update({
|
||||
"messages": [m.serialise() for m in history.messages],
|
||||
"subjects": [s.serialise() for s in history.subjects]
|
||||
"subjects": [s.serialise() for s in history.subjects],
|
||||
})
|
||||
|
||||
history.extra.setdefault("old_versions", []).append(old_version)
|
||||
await self.host.memory.storage.delete(
|
||||
history.messages + history.subjects,
|
||||
session_add=[history]
|
||||
|
||||
history.messages.clear()
|
||||
history.subjects.clear()
|
||||
history.extra["retracted"] = True
|
||||
# we remove editions history to keep no trace of old messages
|
||||
if "editions" in history.extra:
|
||||
del history.extra["editions"]
|
||||
if "attachments" in history.extra:
|
||||
del history.extra["attachments"]
|
||||
await self.host.memory.storage.add(history)
|
||||
|
||||
retract_data = MessageData(history.serialise())
|
||||
self.host.bridge.message_update(
|
||||
history.uid,
|
||||
C.MESS_UPDATE_RETRACT,
|
||||
data_format.serialise(retract_data),
|
||||
client.profile,
|
||||
)
|
||||
|
||||
async def _message_received_trigger(
|
||||
|
|
@ -200,39 +221,48 @@ class XEP_0424(object):
|
|||
message_elt: domish.Element,
|
||||
post_treat: defer.Deferred
|
||||
) -> bool:
|
||||
fastened_elts = await self._f.get_fastened_elts(client, message_elt)
|
||||
if fastened_elts is None:
|
||||
return True
|
||||
for elt in fastened_elts.elements:
|
||||
if elt.name == "retract" and elt.uri == NS_MESSAGE_RETRACT:
|
||||
if fastened_elts.history is not None:
|
||||
source_jid = fastened_elts.history.source_jid
|
||||
from_jid = jid.JID(message_elt["from"])
|
||||
if source_jid.userhostJID() != from_jid.userhostJID():
|
||||
log.warning(
|
||||
f"Received message retraction from {from_jid.full()}, but "
|
||||
f"the message to retract is from {source_jid.full()}. This "
|
||||
f"maybe a hack attempt.\n{message_elt.toXml()}"
|
||||
)
|
||||
return False
|
||||
break
|
||||
else:
|
||||
retract_elt = next(message_elt.elements(NS_MESSAGE_RETRACT, "retract"), None)
|
||||
if not retract_elt:
|
||||
return True
|
||||
try:
|
||||
if message_elt.getAttribute("type") == C.MESS_TYPE_GROUPCHAT:
|
||||
col_id = History.stanza_id
|
||||
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]
|
||||
)
|
||||
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()
|
||||
):
|
||||
log.warning(
|
||||
f"Received message retraction from {from_jid.full()}, but the message to "
|
||||
f"retract is from {history.source_jid.full()}. This maybe a hack "
|
||||
f"attempt.\n{message_elt.toXml()}"
|
||||
)
|
||||
return False
|
||||
|
||||
if not await self.host.trigger.async_point(
|
||||
"XEP-0424_retractReceived", client, message_elt, elt, fastened_elts
|
||||
"XEP-0424_retract_received", client, message_elt, retract_elt, history
|
||||
):
|
||||
return False
|
||||
if fastened_elts.history is None:
|
||||
|
||||
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()}"
|
||||
)
|
||||
return False
|
||||
log.info(f"[{client.profile}] retracting message {fastened_elts.id!r}")
|
||||
await self.retract_db_history(client, fastened_elts.history)
|
||||
# TODO: send bridge signal
|
||||
|
||||
log.info(f"[{client.profile}] retracting message {history.uid!r}")
|
||||
await self.retract_db_history(client, history)
|
||||
return False
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ PLUGIN_INFO = {
|
|||
C.PI_NAME: "Fallback Indication",
|
||||
C.PI_IMPORT_NAME: "XEP-0428",
|
||||
C.PI_TYPE: "XEP",
|
||||
C.PI_MODES: C.PLUG_MODE_BOTH,
|
||||
C.PI_PROTOCOLS: ["XEP-0428"],
|
||||
C.PI_MAIN: "XEP_0428",
|
||||
C.PI_HANDLER: "yes",
|
||||
|
|
|
|||
Loading…
Reference in a new issue