Giter Club home page Giter Club logo

matrix-client.el's Introduction

images/logo64.png matrix-client.el

https://img.shields.io/matrix/matrix-client.el:matrix.org.svg?label=%23matrix-client.el:matrix.org

This is a fork of the original matrix.el client with the intent of maintaining it further, fixing bugs, and providing long-term goals for the project.

Deprecation

This client is deprecated in favor of Ement.el, a newer Matrix client for Emacs. This client will probably receive no further updates.

Contents

Screenshots

images/notifications-buffer-and-room-list_spacemacs-dark.png

Of course, since it’s Emacs, you can use any Emacs theme you like. Here’s django without scroll bars or fringes. Note the syntax highlighting in the pasted code, which respects the HTML sent by the client, rather than overriding it like Riot–for better or worse, you decide. ;)

images/django.png

And here’s the default Emacs theme:

images/frame-purpose-example.png

Installation

There are two ways to use the client:

  1. In a separate Emacs instance as a “standalone client.” This provides easy access to the dedicated-frame and other UI features, and it isolates the client from your main Emacs process (the main benefit of which is to avoid delays while processing large initial sync responses, which can take a little time, depending on how many rooms you’re in and how large they are).
  2. In an existing Emacs process and configuration, like any other Emacs package.

Notes:

  • This client is not available on MELPA. See this discussion for more information.
  • Emacs 26 or later is required.

Standalone client

The standalone client is launched with the script matrix-client-standalone.el.sh (GitHub link). To use, download the script, make it executable, and run it separately–not from within Emacs. If any packages are missing, it uses Emacs’s built-in package.el to install quelpa from MELPA, and quelpa then installs this client package and its dependencies. Then it runs the “standalone client” as a separate Emacs process.

The script accepts these options (use --help for full list):

  • --debug: Enable debug-on-error in the Emacs session.
  • --upgrade: Upgrade the client to the latest version before connecting. (This also upgrades the script in the package directory, but any changes to it won’t take effect until the next time it’s run.)

The script doesn’t load any standard Emacs user configuration files, so it’s almost like a clean Emacs config. However, it uses the default Emacs packages directory (e.g. ~/.emacs.d/elpa): all packages will be installed and read from there, which means that you can load any packages that you already have installed in Emacs, including themes (and after installation you can load the client in your main Emacs config, as well). It also loads and saves its own user-init-file at ~/.config/matrix-client-standalone.el, which preserves customized variables and faces; you can add your own code to it, just as you would to your regular Emacs .emacs or init.el file, which gives you a config mostly independent from your main Emacs config.

To make switching room buffers easy, the default completion settings are improved, and these key bindings are in effect by default:

  • C-<tab>: Switch room buffer.
  • F1: Show only Notifications buffer.
  • M-/: Expand text before point with hippie-expand. Helpful for the /upload command as it dynamically expands filename paths.

There’s also a context menu available in the sidebar by right-clicking rooms.

Of course, all this is an unorthodox way of installing and using an Emacs package, so if you are uncomfortable with this method, please do inspect the code yourself, or use another installation method.

Existing Emacs configuration

matrix-client is not available on MELPA, so it’s recommended to install it with with Quelpa.

  1. Install Quelpa and quelpa-use-package (which can be installed directly from MELPA).
  2. Add this form to your init file:
(use-package matrix-client
  :quelpa (matrix-client :fetcher github :repo "alphapapa/matrix-client.el"
                         :files (:defaults "logo.png" "matrix-client-standalone.el.sh")))

After installation, upgrading can be done through quelpa, e.g. with this command.

Manual installation

  1. Install all dependency packages, which are listed in the Package-Requires header in matrix-client.el.
  2. Put this repo’s directory onto your load-path, and veal (require 'matrix-client).

Usage

If you don’t already have a Matrix account, you can register on a public homeserver such as matrix.org. matrix-client.el doesn’t currently support registration, but you can use another client like Riot to register an account.

Then run the command matrix-client-connect or matrix-client-frame to connect. Customization options are available in the matrix-client group.

Commands

  • List room commands: /help
  • Membership
    • Join a room: /join #room:server
    • Leave the current room: /leave
    • List room users: /who
  • Room settings
    • Set room priority: /priority
    • List or set user-tags: /tags
    • Add user-tags: /tag
    • Delete user-tags: /untag
    • Set notification settings: /notify
      • Note: Notification settings are local to Emacs and do not integrate with the API spec.
  • Sending messages:
    • Send Org-formatted messages: /org
      • Note: Sending Org-formatted messages is the default. You can disable it by changing the option matrix-client-send-as-org-by-default, after which Org messages can be sent with the /org command.
      • You can send almost any Org syntax, including simple emphasis like bold and italic, one-line code blocks with : at the beginning of a line, code emphasis with ~, even Org tables and complete #+BEGIN_SRC lang ... #+END_SRC blocks! Emacs will even send the code blocks colorized according to your theme, and other matrix-client.el users will see the colors!
      • Press C-c ' (or whatever you may have bound org-edit-special to) to edit the current message in a dedicated Org buffer, then save it back to the room input line with C-x C-s (or whatever you may have bound save-buffer to).
    • Send unformatted messages: /raw
      • When matrix-client-send-as-org-by-default is enabled, this sends messages without Org formatting.
    • Send HTML messages: /html
  • Upload a file: /upload PATH-OR-URL, or drag-and-drop files or URLs onto the room buffer
  • Enable pretty colors: /rainbow
  • Other:
    • Reply to messages: With point on a message, r, or R to reply with quote.
    • Complete usernames and IDs: TAB (with point at prompt)
    • Move point between messages: TAB/S-TAB
    • Return point to input prompt: RET (with point before prompt)
    • Open room in a new frame: Middle-click or press <C-return> in the room list.
    • Switch to the notifications buffer: C-c C-n
    • Show room list: /rooms

Notifications buffer

The *Matrix Notifications* buffer displays notifications from all rooms, acting as a sort of meta-buffer. By setting a room’s notifications to always, silent, or silent-unless-mention, you can monitor all messages from that room in the notifications buffer. Press C-c C-n from a room buffer to show the notifications buffer.

You can also reply to messages directly from the notifications buffer. Just as in a room’s buffer, press TAB/S-TAB to navigate between messages, then press r on a message, type your reply, and send by pressing RET. Note that this only allows replies to messages; to send a non-reply message, do so from the room’s buffer.

To jump to a message in the room’s buffer, press RET with point on the message.

Here’s an example of following multiple, related conversations across multiple rooms in the notifications buffer:

images/notifications-buffer.png

Room list

Open the room list by pressing C-c C-r or calling command matrix-client-room-list. Just like in the dedicated frame’s room-list sidebar, you can click rooms to show their buffers, right-click rooms to change their settings, and middle-click rooms to open them in a new frame.

Missing features

matrix-client is very usable, but it does not implement all Matrix features, so it may be necessary to perform some actions from Riot or another client.

Invitations
Room invitations are not currently supported, so they must be sent and accepted elsewhere (this could be fixed with a little work).
Room encryption (E2EE)
Room encryption is not supported, and it probably will not be supported natively, because libolm is not usable from Emacs. However, the Pantalaimon E2EE proxy may be a good solution, and perhaps some integration for it could be added.

Contributing

Contributions are welcome! Please feel free to submit an issue or pull request.

For discussion, feel free to join us in #matrix-client.el:matrix.org.

Authors

  • Ryan Rix: The original author of matrix-client.
  • Adam Porter: Rewrote most of the client and added new features.
  • Jay Kamat: Contributes maintenance and reviews.

License

GPLv3.

matrix-client.el's People

Contributors

aaronraimist avatar alphapapa avatar dakrone avatar gergelypolonkai avatar jental avatar jgkamat avatar non-jedi avatar rrix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

matrix-client.el's Issues

matrix-client-connect raised error in *Message* buffer

error in process filter: mapc: Symbol’s value as variable is void: fn-name
error in process filter: Symbol’s value as variable is void: fn-name

This is the latest version from master branch. (I use Emacs Quelpa with use-package like bellowing)

(use-package matrix-client
  :quelpa ((matrix-client-ng :fetcher github :repo "jgkamat/matrix-client-el") :upgrade t)
  :commands (matrix-client-connect)
  :init (setq matrix-client-ng-save-token t
              matrix-client-use-tracking t
              matrix-client-show-images t))

Access tokens and passwords may be revealed by curl

@jgkamat I guess there's nothing we can do about this, and it's upstream of us anyway, being in request and curl. But when request is using curl, there is a risk that passwords and access tokens may be revealed by temporary files (sometimes they seem to be left behind in /tmp, but I'm not sure when/why), and command line args (i.e. command-line args are visible to all processes on the system, regardless of user ID). If we ever publish this package, we should probably at least note this prominently in the readme, so users can decide if they want to take the risk. I'm not worried about it on my system, but I would want to be informed if I were a user.

Maybe we should also consider reporting a bug on request, in case it can be fixed. If the GET params could always be passed with a file instead of args, that would fix half of it. Then maybe we could figure out why sometimes temp files are left behind, and/or ensure that they are created 0600.

Another possibility would be to investigate url-retrieve, which is built in to Emacs and supports async. But I don't think it has nearly as many useful features as request. However, from what I can tell, it does not use curl but Emacs's native network support, so it might not have these issues (or it might, I really don't know).

Void variable `notify` when byte compiling

Interactively calling either of the (autoloaded) matrix-client-connect and matrix-client-frame functions fails with the error:

require: Symbol’s value as variable is void: notify

after installing the package in Doom Emacs:

$ doom version 2>/dev/null
Doom v2.0.9 (Emacs v26.3)
Branch: develop
Commit: 4b20c7206ef92ac4639c412beca813ebc15e4e88
Build date: 2020-03-13 17:34:44 -0400

with straight.el by adding the following to $DOOMDIR/packages.el:

(package! matrix-client
  :recipe (:host github :repo "alphapapa/matrix-client.el"))

and running doom sync (which byte-compiles the package and its dependencies).

The version of the matrix-client.el package that straight.el installed is, at the time of writing, master's HEAD:

$ (cd $HOME/.emacs.d/.local/straight/repos/matrix-client.el && git rev-parse HEAD)
59c792423fd0503f1375f356bec56130e65d3b51

Requiring 'matrix-client before calling matrix-client-connect or matrix-client-frame does not fix the problem. However, removing all byte-compiled files does:

(cd $HOME/.emacs.d/.local/straight/build/matrix-client && rm -f *.elc)

The problem is that matrix-notifications.el does not require 'matrix-client-room during compilation, so the macro matrix-client-def-room-command is undefined. The solution is to require 'matrix-client-room during compilation:

diff --git a/matrix-notifications.el b/matrix-notifications.el
index 2b44995..140c1b1 100644
--- a/matrix-notifications.el
+++ b/matrix-notifications.el
@@ -34,6 +34,8 @@
 
 (require 'anaphora)
 
+(eval-when-compile (require 'matrix-client-room))
+
 ;;;; Customization
 
 (defgroup matrix-client-notifications nil

While this fixes the problem, matrix-notifications.el uses functions from matrix-client-room.el (matrix-client-update-last-seen, matrix-client--find-propertized-string, and maybe more), so perhaps it would be better to require it at runtime as well, by forgoing eval-when-compile.

I would submit a pull request, but I noticed that this file also uses matrix-room and matrix-user-displayname from matrix-api-r0.3.0.el, and matrix-client-sessions and matrix-client-event-timestamp from matrix-client.el, and maybe more, and as I am unaware of how Emacs handles circular dependencies, I don't know if matrix-notifications.el should require 'matrix-api-r0.3.0 and 'matrix-client.

Issues / vulnerability with E2E encryption

Summary:

  • Messages sent to E2E encrypted rooms are sent cleartext with no warning or indication of what’s happening.
  • Encrypted messages sent to E2E encrypted rooms are silently discarded.

After installing matrix-client.el, I found that some rooms weren't working, while others were. In the broken rooms, I could send messages and see typing notifications, but new messages never appeared. The missing messages show up in Riot, so this is a matrix-client.el issue.

Eventually, I realized that the difference between the working and non-working rooms was whether E2E encryption was enabled. On rooms with E2E encryption, matrix-client.el silently discards any message except my own.

This got me more curious, so I looked at the message detail in Riot, comparing messages I sent with Riot to ones I sent with matrix-client.el. While the Riot messages are encrypted (I can click the "..." at right and see "End-to-end encryption information" in the dialog that pops up), messages sent from matrix-client.el aren’t encrypted at all, even though the room is set to E2E encryption. This deeply violates my expectations about Matrix client behavior -- if a room is encrypted, cleartext messages should NEVER be sent to it. I’m actually surprised that the Matrix server allowed it to happen.

Server rejects messages with certain characters

A few weeks ago I encountered a weird issue where I couldn't send messages with quotation marks, because the server would reject them. IIRC it went away when I restarted Emacs.

Just now I found another message I am unable to send, because the server rejects it with HTTP code 400:

The late André Bensoussan worked with me on the Multics operating system at Honeywell in Cambridge. We were working on a major change to the file system, which required a subsystem, the VTOC manager, to manage file description information. It had to transport the file information between disk and memory, manage a shared memory buffer pool, and manage space on disk for the information. In other words, it was a small virtual memory manager.

(A quote from http://www.multicians.org/andre.html).

I restarted Emacs, but it made no difference. When I replaced the é with an e, the server accepted the message.

I don't know if this is related to the quotation mark issue. I'm guessing it's related to Unicode and/or strict parsing of JSON data by the server. Hopefully it will be a simple fix, like specifying the content-encoding of the request...

Use device IDs

We can set a device ID of our choosing, and cache it along with the access token (#22). Note, though, that this might cause issues with using multiple Emacs processes with a Matrix account at the same time. Normally that would probably be fine, but it might be annoying when developing and debugging.

We can also set a device ID description, something like matrix-client.el @ hostname seems like a good idea. Hostnames aren't generally considered private on the Internet, e.g. they tend to show up in email headers even for hosts behind NAT.

Authenticate via .authinfo.gpg

It would be convenient to authenticate using the credentials inside an .authinfo.gpg file as is done in erc here. This would be (less) useful for users which cache their token and (more) useful for users which do not. Something along the lines of (essentially stolen from erc):

(defun matrix-client-authinfo-password (server user)
  (let ((secret
         (plist-get
          (nth 0
               (auth-source-search :host server
                                   :max 1
                                   :user user
                                   :require '(:secret)))
          :secret)))
    (if (functionp secret)
        (funcall secret)
      secret)))

To be used in matrix-client-connect if it does not return nil.
If you think it is useful I would prepare a PR.

Idea: use minibuffer for input

@jgkamat It just occurred to me: we could probably just use the minibuffer for input. "All" we need to do is bind self-insert-command in room buffers to a command that reads from the minibuffer and inserts the first character. That way we wouldn't need to worry about whether point is positioned at the right place to input text, whether the input line has scrolled off-screen, etc. What do you think?

Helm support

@jgkamat This might be useful, especially for someone who has 150-odd rooms... :)

(defvar helm-source-matrix-client-rooms
  (helm-build-sync-source "Matrix rooms"
    :candidates (lambda ()
                  (cl-loop for (_ . con) in matrix-client-connections
                           append (cl-loop for (_ . room) in (oref con rooms)
                                           for buffer = (oref room buffer)
                                           for name = (buffer-name buffer)
                                           collect (cons name buffer))))
    :action #'switch-to-buffer)
  "Helm source definition for Matrix rooms.")

(defun helm-matrix-rooms ()
  "Show list of Matrix rooms with Helm."
  (interactive)
  (helm :sources helm-source-matrix-client-rooms))

Use ewoc?

@jgkamat I just happened to stumble upon this in the Elisp manual:

The Ewoc package constructs buffer text that represents a structure of Lisp objects, and updates the text to follow changes in that structure. This is like the “view” component in the “model–view–controller” design paradigm. Ewoc means “Emacs’s Widget for Object Collections”.

Theoretically that would be ideal for our room buffers. It could greatly simplify a lot of our code. I wonder if there's a reason I've never heard of it, and why I've never seen any other packages using it...

Assuming server is matrix.org

When connecting, it prompts for server. I give it matrix.org it then
connects but the echo area says "Assuming server is matrix.org". It's
minor, but it shouldn't say "Assuming server is matrix.org" when one
has explicitly giving it the server address.

Patch from Nicolas Goaziou

Nicolas Goaziou was kind enough to fix some byte compiler warnings and other issues in this patch:

From 67ba05d806284ab259cf95ed08d8f6e20cfebb10 Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <[email protected]>
Date: Mon, 21 Jan 2019 01:03:17 +0100
Subject: [PATCH] Silence byte-compiler

---
 matrix-api-r0.3.0.el  | 32 +++++++++++++----------
 matrix-client-room.el | 16 +++++++++---
 matrix-client.el      | 61 ++++++++++++++++++++++---------------------
 matrix-helpers.el     | 13 ++++++++-
 matrix-macros.el      | 15 ++++++-----
 5 files changed, 83 insertions(+), 54 deletions(-)

diff --git a/matrix-api-r0.3.0.el b/matrix-api-r0.3.0.el
index 1120832..78710e2 100644
--- a/matrix-api-r0.3.0.el
+++ b/matrix-api-r0.3.0.el
@@ -75,10 +75,11 @@ method)."
                                           (list 'setq name initer)))
                                       slots)))
          (slot-names (mapcar #'car slots))
-         (around-fn-name (intern (concat (symbol-name name) "-initialize")))
+         ;; FIXME: Unused?
+         ;; (around-fn-name (intern (concat (symbol-name name) "-initialize")))
          (docstring (format "Inititalize instance of %s." name)))
     ;; Add nil initforms
-    (cl-loop for (slot . attrs) in slots
+    (cl-loop for (_slot . attrs) in slots
              unless (plist-get attrs :initform)
              ;; FIXME: Doesn't work if there are no attrs at all.
              do (nconc attrs (list :initform nil)))
@@ -107,6 +108,7 @@ automatically, and other keys are allowed."
                                 error url query ; Used primarily for error handling
                                 &allow-other-keys)
        ,docstring
+       cbargs status headers data error url query ;silence byte-compiler
        (with-slots ,slots ,instance
          ,body))))
 
@@ -437,7 +439,7 @@ set, will be called if the request fails."
            (data (map-filter
                   ;; Remove keys with null values
                   ;; TODO: Benchmark this against cl-loop.
-                  (lambda (k v)
+                  (lambda (_ v)
                     v)
                   data))
            (success (cl-typecase success
@@ -490,7 +492,7 @@ set, will be called if the request fails."
 (cl-defun matrix-request-request (session endpoint &key data success
                                           raw-data (content-type "application/json")
                                           (method "GET") (error #'matrix-request-error-callback) timeout
-                                          (query-on-exit t))
+                                          (_query-on-exit t))
   "Using `request', make request to ENDPOINT on SESSION with DATA and call CALLBACK on success.
 Request is made asynchronously.  METHOD should be a symbol or
 string, `get' (the default) or `post' (it will be upcased).  ENDPOINT may be a string
@@ -522,7 +524,7 @@ set, will be called if the request fails."
            (data (map-filter
                   ;; Remove keys with null values
                   ;; TODO: Benchmark this against cl-loop.
-                  (lambda (k v)
+                  (lambda (_ v)
                     v)
                   data))
            (success (cl-typecase success
@@ -799,7 +801,9 @@ requests, and we make a new request."
                                        (car (push (matrix-room :session session
                                                                :id room-id)
                                                   rooms))))
-                             (prev-batch (a-get* joined-room 'timeline 'prev_batch)))
+                             ;; FIXME: Unused?
+                             ;; (prev-batch (a-get* joined-room 'timeline 'prev_batch))
+                             )
                   (cl-loop for param in params
                            ;; If the event array is empty, the function will be
                            ;; called anyway, so ignore its return value.
@@ -957,7 +961,7 @@ maximum number of events to return (default 10)."
 (matrix-defcallback messages matrix-room
   "Callback for /rooms/{roomID}/messages."
   :slots (id timeline timeline-new timeline-event-ids prev-batch last-full-sync)
-  :body (pcase-let* (((map start end chunk) data)
+  :body (pcase-let* (((map _start end chunk) data)
                      ;; Disable notifications while loading old messages.
                      (matrix-client-notifications nil)
                      (new-events-p))
@@ -1027,19 +1031,19 @@ maximum number of events to return (default 10)."
         ;; Run API handler for event.
         (matrix-event room event)))))
 
-(defun matrix-sync-to_device (session data)
+(defun matrix-sync-to_device (_session _data)
   "Sync to_device data in SESSION."
   ;; FIXME: Implement.
   ;; (matrix-log "Received to_device data: %s" data)
   )
 
-(defun matrix-sync-device_lists (session data)
+(defun matrix-sync-device_lists (_session _data)
   "Sync device_lists data in SESSION."
   ;; FIXME: Implement.
   ;; (matrix-log "Received device_lists data: %s" data)
   )
 
-(defun matrix-sync-unread_notifications (room unread-notifications)
+(defun matrix-sync-unread_notifications (_room _unread-notifications)
   "Sync UNREAD-NOTIFICATIONS in ROOM."
   ;; (pcase-let (((eieio id) room)
   ;;             ((map highlight_count notification_count) unread-notifications))
@@ -1050,7 +1054,7 @@ maximum number of events to return (default 10)."
 
 ;;;;; Rooms
 
-(cl-defun matrix-create-room (session &key (visibility "private") alias name topic invite preset (is-direct t))
+(cl-defun matrix-create-room (session &key (visibility "private") alias name topic _invite preset (is-direct t))
   "Create new room on SESSION.
 When IS-DIRECT is non-nil, set that flag on the new room."
   ;; https://matrix.org/docs/spec/client_server/r0.3.0.html#id190
@@ -1204,7 +1208,7 @@ ACTION may be `put' or `delete', as appropriate."
       (funcall fn session (format$ "user/$user-id/rooms/$room-id/tags/$name")
                :data data
                :success success
-               :error (apply-partially #'matrix-set-tags-error-callback room)
+               :error (or error (apply-partially #'matrix-set-tags-error-callback room))
                :timeout 30))))
 
 (matrix-defcallback set-tags matrix-room
@@ -1281,7 +1285,7 @@ ACTION may be `put' or `delete', as appropriate."
   (pcase-let* ((`(((type . "m.direct")
                    (content . ,users)))
                 (matrix-account-data "m.direct" session)))
-    (cl-loop for (user-id . room-ids) in users
+    (cl-loop for (_user-id . room-ids) in users
              thereis (seq-contains room-ids room-id #'string=))))
 
 ;;;;; Typing notifications
@@ -1314,7 +1318,7 @@ TYPING-P should be t or nil."
 (defun matrix-transform-mxc-uri (session uri)
   "Return HTTPS URL for MXI URI to be accessed through SESSION."
   (pcase-let* (((eieio server) session)
-               (`(,protocol _ ,mxc-server ,file) (split-string uri "/")))
+               (`(,_protocol _ ,mxc-server ,file) (split-string uri "/")))
     (format$ "https://$server/_matrix/media/v1/download/$mxc-server/$file")))
 
 (defun matrix-upload (room path)
diff --git a/matrix-client-room.el b/matrix-client-room.el
index 58ba178..425f188 100644
--- a/matrix-client-room.el
+++ b/matrix-client-room.el
@@ -9,6 +9,7 @@
 (require 'matrix-client-rainbow)
 (require 'ordered-buffer)
 
+(require 'anaphora)
 (require 'dash-functional)
 (require 'esxml-query)
 
@@ -43,22 +44,30 @@ Used to add a button for pending messages.")
     map)
   "Keymap for `matrix-client-mode'.")
 
+(defgroup matrix-client-room nil
+  "Matrix Client room configuration."
+  :group 'matrix-client-room)
+
 (defcustom matrix-client-send-as-org-by-default t
   "Send messages as Org-formatted text by default.
 When disabled, use the \"/org\" command to send Org-formatted
 text."
+  :group 'matrix-client-room
   :type 'boolean)
 
 (defcustom matrix-client-show-room-avatars t
   "Download and show room avatars."
+  :group 'matrix-client-room
   :type 'boolean)
 
 (defcustom matrix-client-show-room-avatars-in-buffer-names t
   "Show room avatars in buffer names."
+  :group 'matrix-client-room
   :type 'boolean)
 
 (defcustom matrix-client-room-avatar-in-buffer-name-size (default-font-height)
   "Size of room avatars in buffer names."
+  :group 'matrix-client-room
   :type '(choice (const :tag "Default font height" default-font-height)
                  (integer :tag "Size in pixels")
                  (function :tag "Custom function (should return integer)"))
@@ -69,6 +78,7 @@ text."
 
 (defcustom matrix-client-timestamp-header-delta 300
   "Number of seconds between messages after which a timestamp header is shown."
+  :group 'matrix-client-room
   :type 'integer)
 
 (defvar matrix-client-room-commands nil
@@ -1110,14 +1120,14 @@ If such text is not found, return nil."
 
 (defun matrix-client--update-date-headers ()
   "Update date headers in current buffer."
-  (cl-flet ((next-header-pos () (matrix--next-property-change (point) 'matrix-client-day-header nil limit)))
+  (cl-flet ((next-header-pos (l) (matrix--next-property-change (point) 'matrix-client-day-header nil l)))
     (save-excursion
       (goto-char (point-min))
       (cl-loop with inhibit-read-only = t
                with limit = (matrix-client--prompt-position)
                with pos = (if (get-text-property (point) 'matrix-client-day-header)
                               (point)
-                            (next-header-pos))
+                            (next-header-pos limit))
                while pos
                for timestamp = (get-text-property pos 'timestamp)
                for new-date-string = (propertize (matrix-client--human-format-date timestamp)
@@ -1129,7 +1139,7 @@ If such text is not found, return nil."
                     (goto-char pos)
                     (setf (buffer-substring (+ 2 (point)) (1- (next-single-property-change (point) 'timestamp nil limit)))
                           new-date-string))
-               do (setq pos (next-header-pos))))))
+               do (setq pos (next-header-pos limit))))))
 
 (defun matrix-client--prompt-position ()
   "Return position of prompt in current buffer."
diff --git a/matrix-client.el b/matrix-client.el
index 58f61a2..cc105df 100644
--- a/matrix-client.el
+++ b/matrix-client.el
@@ -158,36 +158,37 @@ connecting, non-nil."
       (progn
         (message "Already active")
         'already-active)
-    ;; No existing session
-    (if-let ((enabled matrix-client-save-token)
-             (saved (matrix-client-load-token)))
-        ;; Use saved token
-        ;; FIXME: Change "username" to "user" when we no longer need compatibility with old code
-        (setq user (a-get saved 'username)
-              server (a-get saved 'server)
-              access-token (a-get saved 'token)
-              txn-id (a-get saved 'txn-id))
-      ;; Not saved: prompt for username and password
-      (setq user (or user (read-string "User ID: "))
-            password (or password (read-passwd "Password: "))
-            server (or server (read-string "Server: "
-                                           (or (awhen (string-match (rx ":" (group (1+ anything))) user)
-                                                 (match-string 1 user))
-                                               "matrix.org")))))
-    (if access-token
-        ;; Use saved token and call post-login hook
-        (matrix-client-login-hook (matrix-session :user user
-                                                  :server server
-                                                  :access-token access-token
-                                                  :txn-id txn-id
-                                                  :initial-sync-p t
-                                                  :client-data (matrix-client-session-client-data)))
-      ;; Log in with username and password
-      (matrix-login (matrix-session :user user
-                                    :server server
-                                    :initial-sync-p t
-                                    :client-data (matrix-client-session-client-data))
-                    password))))
+    (let (txn-id)
+      ;; No existing session
+      (if-let ((enabled matrix-client-save-token)
+               (saved (matrix-client-load-token)))
+          ;; Use saved token
+          ;; FIXME: Change "username" to "user" when we no longer need compatibility with old code
+          (setq user (a-get saved 'username)
+                server (a-get saved 'server)
+                access-token (a-get saved 'token)
+                txn-id (a-get saved 'txn-id))
+        ;; Not saved: prompt for username and password
+        (setq user (or user (read-string "User ID: "))
+              password (or password (read-passwd "Password: "))
+              server (or server (read-string "Server: "
+                                             (or (awhen (string-match (rx ":" (group (1+ anything))) user)
+                                                   (match-string 1 user))
+                                                 "matrix.org")))))
+      (if access-token
+          ;; Use saved token and call post-login hook
+          (matrix-client-login-hook (matrix-session :user user
+                                                    :server server
+                                                    :access-token access-token
+                                                    :txn-id txn-id
+                                                    :initial-sync-p t
+                                                    :client-data (matrix-client-session-client-data)))
+        ;; Log in with username and password
+        (matrix-login (matrix-session :user user
+                                      :server server
+                                      :initial-sync-p t
+                                      :client-data (matrix-client-session-client-data))
+                      password)))))
 
 (defun matrix-client-login-hook (session)
   "Callback for successful login.
diff --git a/matrix-helpers.el b/matrix-helpers.el
index 4f43ed2..6f24835 100644
--- a/matrix-helpers.el
+++ b/matrix-helpers.el
@@ -29,6 +29,17 @@
 
 ;;; Code:
 
+(defvar url-callback-arguments)
+(defvar url-http-data)
+(defvar url-http-end-of-headers)
+(defvar url-http-extra-headers)
+(defvar url-http-method)
+(defvar url-http-proxy)
+(defvar url-http-target-url)
+
+;; FIXME: Should it be a defcustom?
+(defconst matrix-homeserver-base-url "https://matrix.org")
+
 ;;;; Macros
 
 (defmacro oref* (&rest slots)
@@ -130,7 +141,7 @@ PAIRS should be of the form (SLOT VALUE SLOT VALUE...)."
 
 (defun matrix-client-room-for-id (connection room-id)
   "Return room for ROOM-ID on CONNECTION."
-  (a-get (oref connection :rooms) room-id))
+  (a-get (oref connection rooms) room-id))
 
 (defun matrix-parse-curl-exit-code (error-string)
   "Return exit code from ERROR-STRING as a number, or nil if none found."
diff --git a/matrix-macros.el b/matrix-macros.el
index b8f0376..6f63131 100644
--- a/matrix-macros.el
+++ b/matrix-macros.el
@@ -4,16 +4,19 @@
 (require 'subr-x)
 (require 'url-http)
 
+(defvar url-callback-arguments)
+(defvar url-http-end-of-headers)
+
 (defmacro if-fn-apply (fn-name args else)
   "If FN-NAME is a function, return result of applying ARGS to it, otherwise eval ELSE form.
 FN-NAME should be a string, and is available in the ELSE form as `fn-name'."
   (declare (debug (form listp form)))
-  ;; FIXME: Probably use with-gensyms* here.
-  `(let* ((fn-name ,fn-name)
-          (fn (intern-soft ,fn-name)))
-     (if (functionp fn)
-         (apply fn ,args)
-       ,else)))
+  (let ((name (cl-gensym)))
+    `(let* ((,name ,fn-name)
+            (fn (intern-soft ,name)))
+       (if (functionp fn)
+           (apply fn ,args)
+         ,else))))
 (put 'if-fn-apply 'lisp-indent-function 2)
 
 (defmacro format$ (string &rest objects)
-- 
2.20.1

Pasting the same image URL multiple times may insert at the same position

It just occurred to me that, the way the code is written, if a user pastes the same image URL into a room more than once, the subsequent times may insert the image at the first place the URL appears instead of at the proper place.

We could search backward from the end of the buffer, but even that might not work perfectly because of the async nature of the code.

Maybe we could use the message ID to ensure that we find the right message...

[RFC] Future of matrix-client.el

One of the reasons I switched to matrix was so I can get all of my communications in one place, but it would be really great if I can get it all in Emacs (and that's why we need a good matrix-client.el). I personally have been struggling to use matrix-client because it simply can't keep up with irc clients (and I mostly use matrix to interact with irc anyway).

For transparency, the original source of this repo is here, written by rrix, but is un-maintained at this point.

However, the matrix protocol added encryption support a long time back, and that would be near-impossible to implement in elisp.

This, in combination with the poor quality of matrix-client.el (dropped messages, lack of features, poor extensibility and configuration) warrants a near-complete rewrite (in my opinion), and that's why this is matrix-client-legacy instead of matrix-client.

As alluded in the ReadMe, I think we have two options (or a combination of the two):

  1. Attempt to write a matrix-client which depends on matrix-python-sdk.

    By doing this, we ensure any API changes or new features are handled on the python sdk side, requiring no effort on our part in the long run. However, the pr for e2e has stalled for quite a while, and I'm not sure when it will get in. The sdk is maintained though, so it will get support eventually.

    Implementing this is a challenge in itself, since there aren't easy elisp -> python bindings. The best solution I've heard is to call into a python daemon running on the system. This is probably going to be quite a lot of work, which is why I don't want to do it until we're sure the python sdk is mature/stable enough to support anything we need (which it isn't right now).

  2. Rewrite matrix-client using matrix.el (the rest api library) and a library for user interaction.

    Ideally, this library would handle most of the 'client' features of the matrix client, so this project would only need to focus on gluing the client to matrix.el.

    The library that I've seen used the most is lui from circe, which seems pretty good to me (although, I haven't tried it before).

    A good example of a project that uses the lui is emacs-slack.

If we go with (2), this will probably never see support for e2e, but it will be simpler to implement, and we can re-use a lot of code. (1) will be a much larger project, it's questionable if it will work, but it future-proof's this to future matrix changes. I'm not sure how big loosing e2e support is, right now, I'm only in a couple e2e rooms, but this can grow a lot in the future (there is talk of making it default eventually).

If anyone has any thoughts (@alphapapa?), please let me know! I'm not sure how much time I'll have to work on this (especially in the near future), but I'm sure that it will eventually get done (I hope!)

EIEIO constructor improvements

@jgkamat I want to store the server name and user ID in the connection object (not just the base URL and the user display name). The server name depends on the base URL, so I'd like to derive it when the instance is created, but there doesn't seem to be a way to do this with EIEIO directly. So I've come up with this:

(defclass argh-class ()
  ((base-url :initarg :base-url
             :initform  "https://matrix.org"
             :type string
             :documentation "URI to your Matrix homeserver, defaults to the official homeserver.")
   (server-name :initform nil
                :documentation "Server name, derived from base-url.  e.g. \"matrix.org\" for reference homeserver.")
   (token :initarg :token
          :initform nil
          :documentation "Matrix access_token")
   (txn-id :initarg :txn-id
           :initform 1
           :type integer)))

(defun argh-class-around (fn &rest slots)
  "Create, initialize, and return new object of class type `argh-class'.
Should be defined as :around advice for `argh-class'."
  (let ((obj (apply fn slots)))
    (with-slots (base-url server-name) obj
      (setq server-name (url-host (url-generic-parse-url base-url))))
    obj))

(advice-add 'argh-class :around #'argh-class-around)

(setq argh (argh-class :base-url "https://matrix.org"))

(oref argh server-name)

It works, and we can easily add to the -around function if we want to initialize more derived vars later on.

What do you think? Is there a better way? This doesn't seem too bad, but I would certainly prefer something built-in to EIEIO. I was hoping for a way to define a setter for a slot, so I could define one for the base-url slot that sets the server-name too, but there doesn't seem to be a way. There is a :writer option, but it doesn't seem to be appropriate. It would be easy with Python in the __init__ method, but I can't find an equivalent, so this is as close as I got.

Thanks.

Installation steps for spacemacs

Is there any chance you could make some installation steps specifically for spacemacs? I tried and failed to follow both the "Quelpa" and "Manually" methods to get this going. I don't really know what I'm doing here, so for now I'm just going to put my configs back to how they were before and give up.

Replace add-to-list with...?

Hi Jay,

I noticed that add-to-list is used to add to alists. The Elisp docs recommend against this. For example:

(defmethod matrix-login ((con matrix-connection) login-type arg-list)
  "Attempt to log in to the Matrix homeserver.

LOGIN-TYPE is the value for the `type' key.
ARG-LIST is an alist of additional key/values to add to the submitted JSON."
  (let ((resp (matrix-send con "POST" "/login" (add-to-list 'arg-list (cons "type" login-type)))))
    (oset con :token (cdr (assoc 'access_token resp)))
    resp))

What do you think about replacing these calls? There are several options that I know of.

These are non-destructive:

  • cl-acons
  • a-assoc from a.el (the examples make it appear destructive, but the code appears non-destructive)

These are destructive:

  • push/cl-pushnew with cons
  • asoc-put! from asoc.el

I think, for convenience's sake, I would suggest asoc-put!, as it would make the code cleaner, and the asoc.el library seems pretty nice. a.el is also very nice, but lacks a destructive setter; it would be nice to avoid having to use setq when appropriate.

Package release

I think we should go ahead and get this on MELPA. The client-ng/API-0.3.0 branch can continue, but I don't think we should wait for that to release the package, because as far as I'm concerned, there's no urgency or deadline for that branch to merge to master (although it may not be that far from being ready).

@jgkamat What do you think? :) If you agree, let's put together a checklist for release...

client-ng: Error with multibyte messages

I fixed this problem on master, which uses request.el, but now it's happening with client-ng, which now uses url.el. I don't understand why it's happening.

Trying to send this message triggers it (the special quotation marks are not ASCII):

"""Three logicians walk into a bar.
The bartender asks “So, are you all having drinks?”
The first logician says “I don’t know.”
The second says “I don’t know.”
The third says “Yes!”"""

We encode messages in matrix-send-message:

(a-list 'msgtype msgtype
        'body (encode-coding-string message 'utf-8))

But for some reason, this code in url-http-create-request (using Emacs 26.1) triggers the error:

;; Bug#23750
    (unless (= (string-bytes request)
               (length request))
      (error "Multibyte text in HTTP request: %s" request))

Reading https://debbugs.gnu.org/cgi/bugreport.cgi?bug=23750 doesn't help much, as it seems to be about the error message rather than the underlying problem. And again, I don't understand why this is happening, because we encode it to utf-8, which should solve the problem. For example:

(setq argh "Three logicians walk into a bar.
      The bartender asks “So, are you all having drinks?”
      The first logician says “I don’t know.”
      The second says “I don’t know.”
      The third says “Yes!”")
(multibyte-string-p argh)  ;; => t
(multibyte-string-p (encode-coding-string argh 'utf-8))  ;; => nil

Of course, url.el is testing the length of the string with string-bytes, and Eli Zaretskii explains why in the bug report, but I need to re-read the messages to understand it all. However, since we are not giving url-http-create-request a multibyte string, it seems like the error should not be raised, so maybe this is a new bug in url.el. For example:

(equal (string-bytes (encode-coding-string argh 'utf-8))
       (length (encode-coding-string argh 'utf-8)))  ;; => t

@jgkamat If you have any interest and insight into this, I'd appreciate it. :)

repeatedly hangs emacs

After successfully connecting to matrix.org, I see data being downloaded in a matrix-client "http..." process buffer, and after a while I see buffers for most but not all of my matrix rooms. Emacs keeps alternating between a minute or so unresponsive and a few seconds responsive.

Room buffers are not created

After invoking matrix-client-connect, room buffers are not created. Instead a single buffer named Emacs Matrix Client is created and it displays a single room.

The following messages occur during the connect process

error in process filter: mapc: Symbol’s value as variable is void: fn-name
error in process filter: Symbol’s value as variable is void: fn-name
error in process filter: insert-image: Not an image: nil
error in process filter: Not an image: nil
Invalid face reference: nil [4 times]

Additionally, the following message is shown if I send a message in the buffer's room
matrix-client-send-input: Invalid function: apply-if-fn

Timestamps in notifications buffer use received-time

Just noticed that timestamps in the Notifications buffer use the current wall clock time when the message is entered into the buffer, rather than the message's own timestamp. So e.g. after suspending and resuming a system, when all the messages are retrieved from the next sync, in the notifications buffer, they all have the same timestamp. Should be simple to fix...

Errors using emacs27

When using matrix-client-standalone.el with an emacs compiled from git master, I get a *Messages* buffer with the following:

.emacs.d/elpa/matrix-client-20181219.2042/matrix-api-r0.3.0.el: ‘map-put’ is an obsolete macro (as of 27.1); use map-put! or (setf (map-elt ...) ...) instead [4 times]
Interactive forms unsupported in generic functions: (interactive (list (if current-prefix-arg (read-string "Path/URL: ") (read-file-name "Upload file: " nil nil 'confirm))))
.emacs.d/elpa/matrix-client-20181219.2042/matrix-notifications.el: ‘map-put’ is an obsolete macro (as of 27.1); use map-put! or (setf (map-elt ...) ...) instead [2 times]
.emacs.d/elpa/frame-purpose-20181219.1804/frame-purpose.el: ‘map-put’ is an obsolete macro (as of 27.1); use map-put! or (setf (map-elt ...) ...) instead
delete-other-frames: Attempt to delete the sole visible or iconified frame
error in process filter: Response status unrecognized; please report this error: (:peer
 (:certificates
  ((:version 3 :serial-number "03:36:94:7a:f7:03:d4:c8:94:f6:2c:10:01:b1:59:b0:4e:fc" :issuer "C=US,O=Let's Encrypt,CN=Let's Encrypt Authority X3" :valid-from "2018-10-21" :valid-to "2019-01-19" :subject "CN=dl.matrix.org" :public-key-algorithm "RSA" :certificate-security-level "Medium" :signature-algorithm "RSA-SHA256" :public-key-id "sha1:76:31:aa:47:e4:3c:2a:ae:f7:84:1c:cf:2c:9b:2d:e5:c7:ad:40:36" :certificate-id "sha1:ee:c2:14:e2:2a:8c:29:73:fc:d4:13:01:5c:58:b1:9d:e2:ea:59:f8")
   (:version 3 :serial-number "0a:01:41:42:00:00:01:53:85:73:6a:0b:85:ec:a7:08" :issuer "O=Digital Signature Trust Co.,CN=DST Root CA X3" :valid-from "2016-03-17" :valid-to "2021-03-17" :subject "C=US,O=Let's Encrypt,CN=Let's Encrypt Authority X3" :public-key-algorithm "RSA" :certificate-security-level "Medium" :signature-algorithm "RSA-SHA256" :public-key-id "sha1:da:9b:52:a8:77:11:69:d3:13:18:a5:67:e1:dc:9b:1f:44:b5:b3:5c" :certificate-id "sha1:e6:a3:b4:5b:06:2d:50:9b:33:82:28:2d:19:6e:fe:97:d5:95:6c:cb"))
  :certificate
  (:version 3 :serial-number "03:36:94:7a:f7:03:d4:c8:94:f6:2c:10:01:b1:59:b0:4e:fc" :issuer "C=US,O=Let's Encrypt,CN=Let's Encrypt Authority X3" :valid-from "2018-10-21" :valid-to "2019-01-19" :subject "CN=dl.matrix.org" :public-key-algorithm "RSA" :certificate-security-level "Medium" :signature-algorithm "RSA-SHA256" :public-key-id "sha1:76:31:aa:47:e4:3c:2a:ae:f7:84:1c:cf:2c:9b:2d:e5:c7:ad:40:36" :certificate-id "sha1:ee:c2:14:e2:2a:8c:29:73:fc:d4:13:01:5c:58:b1:9d:e2:ea:59:f8")
  :key-exchange "ECDHE-RSA" :protocol "TLS1.2" :cipher "CHACHA20-POLY1305" :mac "AEAD"))

error in process filter: Response status unrecognized; please report this error: (:peer
 (:certificates
  ((:version 3 :serial-number "03:36:94:7a:f7:03:d4:c8:94:f6:2c:10:01:b1:59:b0:4e:fc" :issuer "C=US,O=Let's Encrypt,CN=Let's Encrypt Authority X3" :valid-from "2018-10-21" :valid-to "2019-01-19" :subject "CN=dl.matrix.org" :public-key-algorithm "RSA" :certificate-security-level "Medium" :signature-algorithm "RSA-SHA256" :public-key-id "sha1:76:31:aa:47:e4:3c:2a:ae:f7:84:1c:cf:2c:9b:2d:e5:c7:ad:40:36" :certificate-id "sha1:ee:c2:14:e2:2a:8c:29:73:fc:d4:13:01:5c:58:b1:9d:e2:ea:59:f8")
   (:version 3 :serial-number "0a:01:41:42:00:00:01:53:85:73:6a:0b:85:ec:a7:08" :issuer "O=Digital Signature Trust Co.,CN=DST Root CA X3" :valid-from "2016-03-17" :valid-to "2021-03-17" :subject "C=US,O=Let's Encrypt,CN=Let's Encrypt Authority X3" :public-key-algorithm "RSA" :certificate-security-level "Medium" :signature-algorithm "RSA-SHA256" :public-key-id "sha1:da:9b:52:a8:77:11:69:d3:13:18:a5:67:e1:dc:9b:1f:44:b5:b3:5c" :certificate-id "sha1:e6:a3:b4:5b:06:2d:50:9b:33:82:28:2d:19:6e:fe:97:d5:95:6c:cb"))
  :certificate
  (:version 3 :serial-number "03:36:94:7a:f7:03:d4:c8:94:f6:2c:10:01:b1:59:b0:4e:fc" :issuer "C=US,O=Let's Encrypt,CN=Let's Encrypt Authority X3" :valid-from "2018-10-21" :valid-to "2019-01-19" :subject "CN=dl.matrix.org" :public-key-algorithm "RSA" :certificate-security-level "Medium" :signature-algorithm "RSA-SHA256" :public-key-id "sha1:76:31:aa:47:e4:3c:2a:ae:f7:84:1c:cf:2c:9b:2d:e5:c7:ad:40:36" :certificate-id "sha1:ee:c2:14:e2:2a:8c:29:73:fc:d4:13:01:5c:58:b1:9d:e2:ea:59:f8")
  :key-exchange "ECDHE-RSA" :protocol "TLS1.2" :cipher "CHACHA20-POLY1305" :mac "AEAD"))

And no matrix client.

Minor formatting bug

This event:

((timestamp . "2018-08-27 19:52:30")
 ((event . matrix-sync-timeline)
  (room-id . "!PWxnIIDhCBAbNItsSN:matrix.org")
  (prev-batch . "s655832779_534597737_366918_165306962_74162268_456336_9778094_8076122_18769")
  (last-full-sync . "s655832779_534597737_366918_165306962_74162268_456336_9778094_8076122_18769")
  (data
   (limited . :json-false)
   (prev_batch . "s655833071_534597938_367056_165307054_74162319_456337_9778095_8076124_18769")
   (events .
           [((origin_server_ts . 1535417546262)
             (sender . "@mattplm:matrix.org")
             (event_id . "$1535417546251346uHStO:matrix.org")
             (unsigned
              (age . 3093))
             (content
              (body . "Hey, Trying to setup a publishing project right now.  I have this problem I don't really understand.\n'''\n(setq org-publish-project-alist\n	'((\"blog\"\n	   :components (\"articles\" \"pages\" \"res\" \"images\"))\n	  (\"articles\"\n	   :base-directory \"~/myrepo\"\n	   :base-extension \"org\"\n	   ;; ...\n	   :html-head-extra ,my/head-extra)\n'''\n\nAnd I get this error: `Wrong type argument: characterp, \\,`.  For reference, I'm using this blog post: https://ogbe.net/blog/blogging_with_org.html as an example.  `my/head-extra` is just a normal string.  Can someone explain my error here ?")
              (msgtype . "m.text")
              (formatted_body . "<p>Hey, Trying to setup a publishing project right now.  I have this problem I don't really understand.<br />'''<br />(setq org-publish-project-alist<br />'((&quot;blog&quot;<br />:components (&quot;articles&quot; &quot;pages&quot; &quot;res&quot; &quot;images&quot;))<br />(&quot;articles&quot;<br />:base-directory &quot;~/myrepo&quot;<br />:base-extension &quot;org&quot;<br />;; ...<br />:html-head-extra ,my/head-extra)<br />'''</p>\n<p>And I get this error: <code>Wrong type argument: characterp, \\,</code>.  For reference, I'm using this blog post: https://ogbe.net/blog/blogging_with_org.html as an example.  <code>my/head-extra</code> is just a normal string.  Can someone explain my error here ?</p>\n")
              (format . "org.matrix.custom.html"))
             (type . "m.room.message"))]))))

Displayed like this in the buffer:

[19:52:26] mattplm> '''
And I get this error: Wrong type argument: characterp, \,. For reference, I'm using this blog post: https://ogbe.net/blog/blogging_with_org.html as an example.
my/head-extra is just a normal string. Can someone explain my error here ?

But the notification popup showed the full message.

Afterward, he reposted the message:

((timestamp . "2018-08-27 19:55:47")
 ((event . matrix-sync-timeline)
  (room-id . "!PWxnIIDhCBAbNItsSN:matrix.org")
  (prev-batch . "s655835025_534599489_367818_165307713_74162636_456337_9778135_8076139_18769")
  (last-full-sync . "s655835025_534599489_367818_165307713_74162636_456337_9778135_8076139_18769")
  (data
   (limited . :json-false)
   (prev_batch . "s655835147_534599591_367871_165307755_74162661_456337_9778146_8076139_18769")
   (events .
           [((origin_server_ts . 1535417746507)
             (sender . "@mattplm:matrix.org")
             (event_id . "$1535417746251916IrvkI:matrix.org")
             (unsigned
              (age . 527))
             (content
              (body . "Hey, trying to setup a publishing project right now.  I have this problem I don't really understand.\n```\n(setq org-publish-project-alist\n	'((\"blog\"\n	   :components (\"articles\" \"pages\" \"res\" \"images\"))\n	  (\"articles\"\n	   :base-directory \"~/myrepo\"\n	   :base-extension \"org\"\n	   ;; ...\n	   :html-head-extra ,my/head-extra)\n```\n\nAnd I get this error: `Wrong type argument: characterp, \\,`.  For reference, I'm using this blog post: https://ogbe.net/blog/blogging_with_org.html as an example. ")
              (msgtype . "m.text")
              (formatted_body . "<p>Hey, trying to setup a publishing project right now.  I have this problem I don't really understand.</p>\n<pre><code>(setq org-publish-project-alist\n	'((&quot;blog&quot;\n	   :components (&quot;articles&quot; &quot;pages&quot; &quot;res&quot; &quot;images&quot;))\n	  (&quot;articles&quot;\n	   :base-directory &quot;~/myrepo&quot;\n	   :base-extension &quot;org&quot;\n	   ;; ...\n	   :html-head-extra ,my/head-extra)\n</code></pre>\n<p>And I get this error: <code>Wrong type argument: characterp, \\,</code>.  For reference, I'm using this blog post: https://ogbe.net/blog/blogging_with_org.html as an example.</p>\n")
              (format . "org.matrix.custom.html"))
             (type . "m.room.message"))]))))

and it rendered like this:

[19:55:46] mattplm> Hey, trying to setup a publishing project right now. I have this problem I don't really understand.
(setq org-publish-project-alist
	'(("blog"
	   :components ("articles" "pages" "res" "images"))
	  ("articles"
	   :base-directory "~/myrepo"
	   :base-extension "org"
	   ;; ...
	   :html-head-extra ,my/head-extra)

And I get this error: Wrong type argument: characterp, \,. For reference, I'm using this blog post: https://ogbe.net/blog/blogging_with_org.html as an example.

server

A few weeks ago I encountered a weird issue where I couldn't send messages with quotation marks, because the server would reject them. IIRC it went away when I restarted Emacs.

Just now I found another message I am unable to send, because the server rejects it with HTTP code 400:

The late André Bensoussan worked with me on the Multics operating system at Honeywell in Cambridge. We were working on a major change to the file system, which required a subsystem, the VTOC manager, to manage file description information. It had to transport the file information between disk and memory, manage a shared memory buffer pool, and manage space on disk for the information. In other words, it was a small virtual memory manager.

(A quote from http://www.multicians.org/andre.html).

I restarted Emacs, but it made no difference. When I replaced the é with an e, the server accepted the message.

I don't know if this is related to the quotation mark issue. I'm guessing it's related to Unicode and/or strict parsing of JSON data by the server. Hopefully it will be a simple fix, like specifying the content-encoding of the request...

Hide old events on connect

When the client is connected and rooms are shown, several events are shown as if they just happened (joins/parts, room name and topic, avatar, etc). These events should not be shown if they happened before the client connected, and the API docs concur. Probably the easiest way to do this is to record the timestamp of when the client connects, and any non-message events from before that timestamp should be ignored (or if it's necessary to process the event to apply it to the room state, it should at least not be shown as a message in the room).

[I've been aware of this issue for some time, but I'm posting this issue now because the easy solution just occurred to me.]

void function esxml-query-all

After I started with matrix-client-ng-connect. I got error:

error in process filter: matrix-client-ng--shr-mx-reply: Symbol’s function definition is void: esxml-query-all
error in process filter: Symbol’s function definition is void: esxml-query-all
uncompressing publicsuffix.txt.gz...done
Quit
user-error: Couldn’t identify where matrix-client is defined
rg finished (2 matches found)
user-error: Couldn’t find definition for function esxml-query-all

It might should be xml-query-all. I search on my Emacs, this is the function name.

And because I don't know which branch is currently active, so I don't know which branch to use and add PR. So I post issue here.

Missing if-let* macro in Emacs 25

I'm not sure when the if-let* macro was added, but I'm running Emacs 25 (which matrix-client.el claims to be compatible with) and it doesn't have this macro. Should the requirements be bumped to 26, or should the code get rewritten to use regular if-let? (There are only 3 uses that aren't already compatible with if-let.)

easier install needed

This sounds great, I'd like to try it out, but my installation's not going too well:

  • Not found in MELPA. So I git clone.

  • I just fixed up my emacs installation and I don't want to risk running some non-standard install script that installs another emacs

  • I've never heard of quelpa, so I check it out and their README says it's retired - sounds like a dead end

  • So it's manual installation for me. I try my usual fumble-around-with-dired technique: open the directory, select all .el files, B, L. 6 files fail to compile. The log shows lots of warnings and errors but I can't see which packages it might be needing.

  • I read and follow the manual instructions more carefully. I add the matrix-client.el directory to load-path and try (require 'matrix-client). Now I see it's failing on require(ov). I work through these errors / look at the source and install a bunch of things with list-packages: a, anaphora, dash-functional, esxml, ov, request tracking.

  • Now it's getting further but fails thusly:

Debugger entered--Lisp error: (void-variable notify)
  byte-code("\304\010\305\306\307\310\011\n\311\312$\211\203\033\0\313\013\002\"\210\314\315\n\"\202_\0\316\013!\211\317\001\320\"\262\001\211\013\001H\262\001\211\002\004\316\003!\211\317\001\321\"\262\001\211\005\001H\262\001\211\002\004\002\322\001\011\323\312$@\324\325\326\327\011\"\"\314\330\003\003#\266\203\266\203\266\203\266\203\266\203\262\001%\207" [notify matrix-client-notification-rules input room matrix-client-def-room-command :docstring "Set room notification setting.\nWithout argument, displays help and current setting." :insert map-elt nil equal matrix-client-set-notification-rule format "Notification rule set: %s." eieio-pcase-slot-index-table eieio-pcase-slot-index-from-index-table client-data notification-rules cl-rassoc :test s-join ", " -map car "Current notification rule: %s.  Available rules: %s."] 25)
  require(matrix-notifications)
  eval-buffer(#<buffer  *load*> nil "/Users/simon/src/CHAT/matrix-client.el/matrix-client.el" nil t)  ; Reading at buffer position 1889
  load-with-code-conversion("/Users/simon/src/CHAT/matrix-client.el/matrix-client.el" "/Users/simon/src/CHAT/matrix-client.el/matrix-client.el" nil t)
  require(matrix-client)
  eval((require (quote matrix-client)) nil)
  eval-expression((require (quote matrix-client)) nil nil 127)
  funcall-interactively(eval-expression (require (quote matrix-client)) nil nil 127)
  call-interactively(eval-expression nil nil)
  command-execute(eval-expression)

Shouldn't this be a bit easier ?

Refactor sidebar to not require frame-purpose library

I'm trying to open the nice-looking roomlist in my current emacs window and I can't see to figure out a command to open that room list. The best I got was to use ace-window to move it from one frame to the other, but now I'm trying to make a new custom layout for matrix so I'm back to square 1.

Cache token

The Riot Android client shows hundreds and hundreds of "devices" for my account, presumably because of all the many times I've logged in with username/password from Emacs while testing.

So we should probably cache the access_token and use it instead of logging in from scratch every time. Of course, if this fails, we need to log in with username and password again.

I guess we should store the token in the authinfo file, using the Emacs functions for it, but I haven't used these before, myself.

Note, though, that if we use a token from multiple Emacs processes simultaneously, I'm not sure what the effect will be. The API docs mentioned something about the server possibly restricting them to one per device. This may tie in with #23.

Problem with some character in password

Hello,
First thank you for maintaining this packages, I just installed it today. However I had a small problem my password was containing special characters like : " è à. This led to a "Login failed : error nil" error.

I "fixed" it by just changing my password to something without those characters and now it works.

Unable to connect to non-matrix.org home server

I'm attempting to connect to my account at matrix.cybre.space via M-x matrix-client-connect, but I'm getting a lisp error. I input my username, password, and home server URL and then the error occurs. Here's the backtrace:

Debugger entered--Lisp error: (wrong-type-argument listp t)
  matrix-url-with-retrieve-async("https://matrix.cybre.space/_matrix/client/r0/login" :query-on-exit t :silent t :inhibit-cookies t :method "POST" :extra-headers (("Content-Type" . "application/json") ("Authorization" . "Bearer ")) :data "{\"type\":\"m.login.password\",\"user\":\"@jdormit:matrix.cybre.space\",\"password\":\"<password>\",\"device_id\":\"fa93856a2633763c504f023bff8ba9b1\",\"initial_device_display_name\":\"matrix-client.el @ jdormit-laptop\"}" :parser json-read :success #f(compiled-function (&rest args2) #<bytecode 0x4f5d265>) :error #f(compiled-function (&rest args2) #<bytecode 0x5534e7d>) :timeout nil)
  matrix-request(#<matrix-session matrix-session> login :data ((type . "m.login.password") (user . "@jdormit:matrix.cybre.space") (password . "<password>") (device_id . "fa93856a2633763c504f023bff8ba9b1") (initial_device_display_name . "matrix-client.el @ jdormit-laptop")) :success matrix-login-callback :error #f(compiled-function (&rest args2) #<bytecode 0x5534e7d>) :method post)
  apply(matrix-request (#<matrix-session matrix-session> login :data ((type . "m.login.password") (user . "@jdormit:matrix.cybre.space") (password . "<password>") (device_id . "fa93856a2633763c504f023bff8ba9b1") (initial_device_display_name . "matrix-client.el @ jdormit-laptop")) :success matrix-login-callback :error #f(compiled-function (&rest args2) #<bytecode 0x5534e7d>) :method post))
  matrix-post(#<matrix-session matrix-session> login :data ((type . "m.login.password") (user . "@jdormit:matrix.cybre.space") (password . "<password>") (device_id . "fa93856a2633763c504f023bff8ba9b1") (initial_device_display_name . "matrix-client.el @ jdormit-laptop")) :success matrix-login-callback :error #f(compiled-function (&rest args2) #<bytecode 0x5534e7d>))
  matrix-login(#<matrix-session matrix-session> "<password>")
  matrix-client-connect()
  funcall-interactively(matrix-client-connect)
  #<subr call-interactively>(matrix-client-connect record nil)
  apply(#<subr call-interactively> matrix-client-connect (record nil))
  call-interactively@ido-cr+-record-current-command(#<subr call-interactively> matrix-client-connect record nil)
  apply(call-interactively@ido-cr+-record-current-command #<subr call-interactively> (matrix-client-connect record nil))
  call-interactively(matrix-client-connect record nil)
  command-execute(matrix-client-connect record)
  execute-extended-command(nil "matrix-client-connect")
  amx-read-and-run((<my command history>))
  amx()
  funcall-interactively(amx)
  #<subr call-interactively>(amx nil nil)
  apply(#<subr call-interactively> amx (nil nil))
  call-interactively@ido-cr+-record-current-command(#<subr call-interactively> amx nil nil)
  apply(call-interactively@ido-cr+-record-current-command #<subr call-interactively> (amx nil nil))
  call-interactively(amx nil nil)
  command-execute(amx)

Async?

Hi,

Thanks for forking this. It seems pretty nice so far.

Seems like one of the first issues is that sending messages is synchronous, so after hitting RET to send a message, Emacs is blocked until the server accepts it.

I wonder if the async and/or deferred libraries could be used for this. What do you think?

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.