Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replies without replied-to content in reply body #57

Open
9viz opened this issue Mar 25, 2022 · 6 comments · May be fixed by #150
Open

Replies without replied-to content in reply body #57

9viz opened this issue Mar 25, 2022 · 6 comments · May be fixed by #150
Assignees
Labels
Milestone

Comments

@9viz
Copy link
Contributor

9viz commented Mar 25, 2022

Some clients don't have the replied to message in body or format_body fields. So one has to rely on the event_id cited in m.relates_to->m.in_reply_to. Example of such an event is (I stripped out the id),

((:id . "ID")
 (:sender . "@_discord_296842705454235650:t2bot.io")
 (:content
  (body . "sheesh u passed out at 4pm?")
  (format . "org.matrix.custom.html")
  (formatted_body . "sheesh u passed out at 4pm?")
  (m\.relates_to
   (m\.in_reply_to
	(event_id . "$TluJm8weVHXI3gwsD-kSeI1HM3UOGIzqe8b-ybmdyb4")))
  (msgtype . "m.text"))
 (:origin-server-ts . 1648191029121)
 (:type . "m.room.message")
 (:state-key)
 (:unsigned))

This is from the t2bot bridging a discord channel and a matrix room.

I can take a stab at implementing this on May.

@alphapapa
Copy link
Owner

Hm, I don't know about this. The spec, in section 13.2.2.6.1 Rich replies, says that the reply event's body is supposed to include the fallback content, i.e. the quoted text. That would seem to suggest that the bot (or any other such client) is not behaving according to spec. In that case, I'd rather the misbehaving client be fixed than write workarounds in this client for bugs in other clients (to a reasonable extent, anyway--there will always be some bugs in other clients, and some workarounds will be needed, but I'd like to keep them to a minimum).

@alphapapa alphapapa changed the title Ement should handle m.relates_to/m.in_reply_to Some clients don't send replies according to spec Mar 26, 2022
@alphapapa
Copy link
Owner

I don't have the link handy, but a recent MSC proposal I saw would require not including the replied-to content in the reply body (in order to avoid leaks of message content to users who joined a room after a replied-to message was sent). So this may become the standard in the future.

@alphapapa alphapapa changed the title Some clients don't send replies according to spec Replies without replied-to content in reply body May 29, 2022
@alphapapa alphapapa added this to the Future milestone Sep 9, 2022
@9viz
Copy link
Contributor Author

9viz commented Oct 7, 2022

FYI, in a couple of hours, I came up with the following super-janky patch that supports rich replies when the room uses formatted body. It hasn't gotten a lot of testing though, and most of the meat is from m.image. I followed the following two parts of the matrix spec to implement it,

diff --git a/ement-room.el b/ement-room.el
index 1c7666e..24daef8 100644
--- a/ement-room.el
+++ b/ement-room.el
@@ -3181,22 +3181,29 @@ Format defaults to `ement-room-message-format-spec', which see."
 If FORMATTED-P, return the formatted body content, when available."
   (pcase-let* (((cl-struct ement-event content
                            (unsigned (map ('redacted_by unsigned-redacted-by)))
-                           (local (map ('redacted-by local-redacted-by))))
+                           (local (map ('redacted-by local-redacted-by)))
+                           (local (map ('reply reply-event))))
                 event)
                ((map ('body main-body) msgtype ('format content-format) ('formatted_body formatted-body)
                      ('m.relates_to (map ('rel_type rel-type)))
+                     ('m.relates_to (map ('m.in_reply_to (map ('event_id reply-event-id)))))
                      ('m.new_content (map ('body new-body) ('formatted_body new-formatted-body)
                                           ('format new-content-format))))
                 content)
                (body (or new-body main-body))
                (formatted-body (or new-formatted-body formatted-body))
+               (reply-in-body-p t)
                (body (if (or (not formatted-p) (not formatted-body))
                          ;; Copy the string so as not to add face properties to the one in the struct.
                          (copy-sequence body)
                        (pcase (or new-content-format content-format)
                          ("org.matrix.custom.html"
                           (save-match-data
-                            (ement-room--render-html formatted-body)))
+                            (setq reply-in-body-p (string-match-p "<mx-reply>" formatted-body))
+                            (ement-room--render-html
+                             (if (and reply-event (null reply-in-body-p))
+                                 (ement-room--rich-reply-text ement-room reply-event formatted-body)
+                               formatted-body))))
                          (_ (format "[unknown body format: %s] %s"
                                     (or new-content-format content-format) body)))))
                (appendix (pcase msgtype
@@ -3206,10 +3213,25 @@ If FORMATTED-P, return the formatted body content, when available."
                            ("m.file" (ement-room--format-m.file event))
                            (_ (if (or local-redacted-by unsigned-redacted-by)
                                   nil
-                                (format "[unsupported msgtype: %s]" msgtype ))))))
+                                (format "[unsupported msgtype: %s]" msgtype))))))
     (when body
       ;; HACK: Once I got an error when body was nil, so let's avoid that.
       (setf body (ement-room--linkify-urls body)))
+    (when (and reply-event-id
+               (not reply-in-body-p)
+               (not reply-event))
+      ;; During initial sync, `ement-ewoc' maybe nil.
+      (if-let ((node (and ement-ewoc
+                          (ement-room--ewoc-last-matching ement-ewoc
+                            (lambda (data)
+                              (and (ement-event-p data)
+                                   (equal (ement-event-id data) reply-event-id)))))))
+          (progn
+            (message "ement: using old event for reply")
+            (setf (map-elt (ement-event-local event) 'reply) (ewoc-data node)
+                  body (ement-room--rich-reply-text ement-room (ewoc-data node) body)))
+        (ement-api ement-session (format "rooms/%s/event/%s" (ement-room-id ement-room) reply-event-id)
+          :then (apply-partially #'ement-room--rich-reply-callback ement-room event))))
     ;; HACK: Ensure body isn't nil (e.g. redacted messages can have empty bodies).
     (unless body
       (setf body (copy-sequence
@@ -3228,6 +3250,35 @@ If FORMATTED-P, return the formatted body content, when available."
       (setf body (concat body " " (propertize "[edited]" 'face 'font-lock-comment-face))))
     body))
 
+(defun ement-room--rich-reply-callback (room event reply-event)
+  (pcase-let* (((cl-struct ement-room (local (map buffer))) room))
+    (setf (map-elt (ement-event-local event) 'reply) (ement--make-event reply-event))
+    (when (buffer-live-p buffer)
+      (with-current-buffer buffer
+        (when-let ((node (ement-room--ewoc-last-matching ement-ewoc
+                           (lambda (data) (eq data event)))))
+          (ewoc-invalidate ement-ewoc node))))))
+
+(defun ement-room--rich-reply-text (room reply-event body)
+  (format
+   "<mx-reply><blockquote>
+    <a href=\"https://matrix.to/#/%s/%s\">In reply to</a>
+    <a href=\"https://matrix.to/#/%s\">%s</a>
+    <br />
+%s
+  </blockquote></mx-reply>
+%s"
+   (ement-room-id ement-room)
+   (ement-event-id reply-event)
+   (ement-user-id (ement-event-sender reply-event))
+   (or (ement-user-displayname (ement-event-sender reply-event))
+       (ement-user-id (ement-event-sender reply-event)))
+   (let ((content (ement-event-content reply-event)))
+     (if (equal (map-elt content 'format) "org.matrix.custom.html")
+         (map-elt content 'formatted_body)
+       (map-elt content 'body)))
+   body))
+
 (defun ement-room--render-html (string)
   "Return rendered version of HTML STRING.
 HTML is rendered to Emacs text using `shr-insert-document'."

@alphapapa
Copy link
Owner

@Vizs Looks promising. It would be easier to review if it were a PR. :)

@9viz
Copy link
Contributor Author

9viz commented Oct 8, 2022

I will do that once I finish up the support for plain text bodies as well. I posted it here in case if it will be of help to Someone(TM).

9viz pushed a commit to 9viz/ement.el that referenced this issue Apr 17, 2023
* ement-room.el (ement-room--rich-reply-callback): Function to modify
the message event when reply event is finally fetched.
(ement-room--rich-reply-text, ement-room--rich-reply-html): Helper
functions for formatting body text with no reply message.
(ement-room--format-message-body): Use above to support rich replies.

Closes alphapapa#57.
Ref. https://spec.matrix.org/v1.4/client-server-api/#rich-replies
@9viz 9viz linked a pull request May 18, 2023 that will close this issue
9viz pushed a commit to 9viz/ement.el that referenced this issue May 18, 2023
* ement-room.el (ement-room--rich-reply-callback): Function to modify
the message event when reply event is finally fetched.
(ement-room--rich-reply-text, ement-room--rich-reply-html): Helper
functions for formatting body text with no reply message.
(ement-room--format-message-body): Use above to support rich replies.

Closes alphapapa#57.
Ref. https://spec.matrix.org/v1.4/client-server-api/#rich-replies
@alphapapa alphapapa modified the milestones: Future, 0.10 May 19, 2023
@alphapapa alphapapa added the enhancement New feature or request label May 19, 2023
@alphapapa alphapapa self-assigned this May 19, 2023
@alphapapa alphapapa modified the milestones: 0.10, 0.11 Jun 14, 2023
@alphapapa alphapapa modified the milestones: 0.11, 0.12 Jul 9, 2023
alphapapa pushed a commit to 9viz/ement.el that referenced this issue Sep 2, 2023
* ement-room.el (ement-room--rich-reply-callback): Function to modify
the message event when reply event is finally fetched.
(ement-room--rich-reply-text, ement-room--rich-reply-html): Helper
functions for formatting body text with no reply message.
(ement-room--format-message-body): Use above to support rich replies.

Closes alphapapa#57.
Ref. https://spec.matrix.org/v1.4/client-server-api/#rich-replies
alphapapa pushed a commit to 9viz/ement.el that referenced this issue Sep 2, 2023
* ement-room.el (ement-room--rich-reply-callback): Function to modify
the message event when reply event is finally fetched.
(ement-room--rich-reply-text, ement-room--rich-reply-html): Helper
functions for formatting body text with no reply message.
(ement-room--format-message-body): Use above to support rich replies.

Closes alphapapa#57.
Ref. https://spec.matrix.org/v1.4/client-server-api/#rich-replies
@alphapapa alphapapa modified the milestones: 0.12, 0.13 Sep 3, 2023
@alphapapa alphapapa modified the milestones: 0.13, 0.14 Oct 3, 2023
@alphapapa alphapapa modified the milestones: 0.14, 0.15 Jan 25, 2024
alphapapa pushed a commit to 9viz/ement.el that referenced this issue Jan 28, 2024
* ement-room.el (ement-room--rich-reply-callback): Function to modify
the message event when reply event is finally fetched.
(ement-room--rich-reply-text, ement-room--rich-reply-html): Helper
functions for formatting body text with no reply message.
(ement-room--format-message-body): Use above to support rich replies.

Closes alphapapa#57.
Ref. https://spec.matrix.org/v1.4/client-server-api/#rich-replies
@alphapapa alphapapa removed this from the 0.15 milestone Mar 31, 2024
@alphapapa alphapapa added this to the 0.16 milestone Mar 31, 2024
@alphapapa
Copy link
Owner

alphapapa commented May 29, 2024

For future reference, in case this MSC is merged: matrix-org/matrix-spec-proposals#2781

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants