diff --git a/README.org b/README.org index 421c948..e8bd0d7 100644 --- a/README.org +++ b/README.org @@ -6,7 +6,7 @@ # [[https://melpa.org/#/package-name][file:https://melpa.org/packages/package-name-badge.svg]] [[https://stable.melpa.org/#/package-name][file:https://stable.melpa.org/packages/package-name-badge.svg]] -Ement.el is a new Matrix client for Emacs. It's basic at the moment, but it can be used to send and read messages (including sending replies and seeing images), join and leave rooms, etc. +Ement.el is a new Matrix client for Emacs. It's basic at the moment, but it can be used to send and read messages (including replies and images), join and leave rooms, etc. * Screenshots @@ -92,6 +92,7 @@ If you want to install it manually, it's simple enough, but you should know what - ~ement-join-room~ to join a room. - ~ement-leave-room~ to leave a room. - ~ement-room-edit-message~ to edit a message at point. + - ~ement-room-send-image~ to send an image message. In a room buffer: diff --git a/ement-room.el b/ement-room.el index 881b966..9bc76c9 100644 --- a/ement-room.el +++ b/ement-room.el @@ -36,6 +36,7 @@ (require 'color) (require 'ewoc) +(require 'mailcap) (require 'shr) (require 'subr-x) (require 'mwheel) @@ -371,6 +372,46 @@ Matrix-related commands in it will fail." ;;;; Commands +(declare-function ement-upload "ement" t t) +(defun ement-room-send-image (file body room session) + "Send image FILE to ROOM on SESSION, using message BODY." + ;; TODO: Support URLs to remote files. + (interactive (let* ((file (read-file-name (format "Send image file (%s): " (ement-room-display-name ement-room)) + nil nil 'confirm)) + (body (read-string (format "Message body (%s): " (ement-room-display-name ement-room)) + file))) + (list file body ement-room ement-session))) + (when (yes-or-no-p (format "Upload file %S to room %S? " + file (ement-room-display-name room))) + (pcase-let* ((extension (file-name-extension file)) + (mime-type (mailcap-extension-to-mime extension)) + (data (with-temp-buffer + ;; NOTE: Using (set-buffer-multibyte nil) doesn't + ;; seem to be necessary, but I don't know why not. + (insert-file-contents file) + (buffer-string))) + (size (length data))) + ;; MAYBE: Send typing notification (maybe make an ement-room-with-typing macro). + (ement-upload session :data data :content-type mime-type + :then (lambda (data) + (message "Uploaded file %S. Sending message..." file) + (pcase-let* (((map ('content_uri content-uri)) data) + ((cl-struct ement-session server token) session) + ((cl-struct ement-room (id room-id)) room) + (endpoint (format "rooms/%s/send/%s/%s" (url-hexify-string room-id) + "m.room.message" (cl-incf (ement-session-transaction-id session)))) + ;; TODO: Image height/width (maybe not easy to get in Emacs). + (data (ement-alist "msgtype" "m.image" + "url" content-uri + "body" body + "info" (ement-alist "mimetype" mime-type + "size" size)))) + (ement-api server token endpoint + (lambda (&rest args) + (message "SEND MESSAGE CALLBACK: %S" args)) + :data (json-encode data) + :method 'put))))))) + (defun ement-room-scroll-up-mark-read () "Scroll buffer up, marking read and burying when at end." (interactive) diff --git a/ement.el b/ement.el index 0008c6e..91242a2 100644 --- a/ement.el +++ b/ement.el @@ -228,6 +228,19 @@ call `pop-to-buffer'." (ement-room--buffer session room (ement--room-buffer-name room))) (goto-char (point-max))) +(cl-defun ement-upload (session &key data filename then else + (content-type "application/octet-stream")) + "Upload DATA with FILENAME to content repository on SESSION. +THEN and ELSE are passed to `ement-api', which see." + (declare (indent defun)) + (pcase-let* (((cl-struct ement-session server token) session) + (endpoint (if filename + (format "upload?filename=%s" (url-hexify-string filename)) + "upload"))) + (ement-api server token endpoint then :else else + :method 'post :endpoint-category "media" + :content-type content-type :data data :data-type 'binary))) + ;;;; Functions (defun ement--sync-messages-p (session)