branch: externals/ement
commit d4c45da59fcad890105e599e1fb15c9c8ba10e01
Author: Adam Porter <[email protected]>
Commit: Adam Porter <[email protected]>

    Add: (ement-room-set-notifications)
---
 README.org    |  2 ++
 ement-lib.el  | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ement-room.el |  2 ++
 3 files changed, 102 insertions(+)

diff --git a/README.org b/README.org
index 43432f6ea3..4f60f5a71e 100644
--- a/README.org
+++ b/README.org
@@ -200,6 +200,7 @@ These bindings are common to all of the following buffer 
types:
 + List members: ~r m~
 + Set topic: ~r t~
 + Set message format: ~r f~
++ Set notification rules: ~r n~
 + Tag/untag room: ~r T~
 
 *Room membership*
@@ -290,6 +291,7 @@ Note that, while ~matrix-client~ remains usable, and 
probably will for some time
 
 *Additions*
 + Option ~ement-room-unread-only-counts-notifications~, now enabled by 
default, causes rooms' unread status to be determined only by their 
notification counts (which are set by the server and depend on rooms' 
notification settings).
++ Command ~ement-room-set-notifications~ sets a room's notification rules 
(imitating Element's user-friendly presets).
 
 *Changes*
 + When a room's read receipt is updated, the room's buffer is also marked as 
unmodified.  (In concert with the new option, this makes rooms' unread status 
more intuitive.)
diff --git a/ement-lib.el b/ement-lib.el
index c0fae8bb93..10474bc7af 100644
--- a/ement-lib.el
+++ b/ement-lib.el
@@ -420,6 +420,104 @@ If DELETE (interactively, with prefix), delete it."
       :then (lambda (data)
               (ement-debug "Changed tag on room" method tag data room)))))
 
+;;;;;; Push rules
+
+;; NOTE: Although v1.4 of the spec is available and describes setting the push 
rules using
+;; the "v3" API endpoint, the Element client continues to use the "r0" 
endpoint, which is
+;; slightly different.  This implementation will follow Element's initially, 
because the
+;; spec is not simple, and imitating Element's requests will make it easier.
+
+(defun ement-room-set-notifications (rule room session)
+  "Set notification RULE for ROOM on SESSION.
+RULE may be nil to set the rules to default, `all',
+`mentions-and-keywords', or `none'."
+  ;; This merely attempts to reproduce the behavior of Element's simple 
notification
+  ;; options.  It does not attempt to offer all of the features defined in the 
spec.  And,
+  ;; yes, it is rather awkward, having to sometimes* make multiple requests of 
different
+  ;; "kinds" to set the rules for a single room, but that is how the API works.
+  ;;
+  ;; * It appears that Element only makes multiple requests of different kinds 
when
+  ;; strictly necessary, but coding that logic now would seem likely to be a 
waste of
+  ;; time, given that Element doesn't even use the latest version of the spec 
yet.  So
+  ;; we'll just do the "dumb" thing and always send requests of both 
"override" and
+  ;; "room" kinds, which appears to Just Work™.
+  ;;
+  ;; TODO: Match rules to these user-friendly notification states for 
presentation.  See
+  ;; 
<https://github.com/matrix-org/matrix-react-sdk/blob/8c67984f50f985aa481df24778078030efa39001/src/RoomNotifs.ts>.
+  (interactive
+   (pcase-let* ((`(,room ,session) (or (when (bound-and-true-p ement-room)
+                                         (list ement-room ement-session))
+                                       (ement-complete-room)))
+                (prompt (format "Set notification rules for %s: " 
(ement--format-room room)))
+                (available-rules (ement-alist "Default" nil
+                                              "All messages" 'all
+                                              "Mentions and keywords" 
'mentions-and-keywords
+                                              "None" 'none))
+                (selected-rule (completing-read prompt (mapcar #'car 
available-rules) nil t))
+                (rule (alist-get selected-rule available-rules nil nil 
#'equal)))
+     (list rule room session)))
+  (cl-labels ((set-rule (kind rule queue message-fn)
+                        (pcase-let* (((cl-struct ement-room (id room-id)) room)
+                                     (rule-id (url-hexify-string room-id))
+                                     (endpoint (format 
"pushrules/global/%s/%s" kind rule-id))
+                                     (method (if rule 'put 'delete))
+                                     (then (if rule
+                                               ;; Setting rules requires 
PUTting the rules, then making a second request to enable them.
+                                               (lambda (_data)
+                                                 (ement-api session (concat 
endpoint "/enabled") :queue queue :version "r0"
+                                                   :method 'put :data 
(json-encode (ement-alist 'enabled t))
+                                                   :then message-fn))
+                                             message-fn)))
+                          (ement-api session endpoint :queue queue :method 
method :version "r0"
+                            :data (json-encode rule)
+                            :then then
+                            :else (lambda (plz-error)
+                                    (pcase-let* (((cl-struct plz-error 
response) plz-error)
+                                                 ((cl-struct plz-response 
status) response))
+                                      (pcase status
+                                        (404 (pcase rule
+                                               (`nil
+                                                ;; Room already had no rules, 
so none being found is not an
+                                                ;; error.
+                                                nil)
+                                               (_ ;; Unexpected error: 
re-signal.
+                                                (ement-api-error plz-error))))
+                                        (_ ;; Unexpected error: re-signal.
+                                         (ement-api-error plz-error)))))))))
+    (pcase-let* ((available-rules
+                  (ement-alist
+                   nil (ement-alist
+                        "override" nil
+                        "room" nil)
+                   'all (ement-alist
+                         "override" nil
+                         "room" (ement-alist
+                                 'actions (vector "notify" (ement-alist
+                                                            'set_tweak "sound"
+                                                            'value 
"default"))))
+                   'mentions-and-keywords (ement-alist
+                                           "override" nil
+                                           "room" (ement-alist
+                                                   'actions (vector 
"dont_notify")))
+                   'none (ement-alist
+                          "override" (ement-alist
+                                      'actions (vector "dont_notify")
+                                      'conditions (vector (ement-alist
+                                                           'kind "event_match"
+                                                           'key "room_id"
+                                                           'pattern 
(ement-room-id room))))
+                          "room" nil)))
+                 (kinds-and-rules (alist-get rule available-rules nil nil 
#'equal)))
+      (cl-loop with queue = (make-plz-queue :limit 1)
+               with total = (1- (length kinds-and-rules))
+               for count from 0
+               for message-fn = (if (equal count total)
+                                    (lambda (_data)
+                                      (message "Set notification rules for 
room: %s" (ement--format-room room)))
+                                  #'ignore)
+               for (kind . rule) in kinds-and-rules
+               do (set-rule kind rule queue message-fn)))))
+
 ;;;;; Public functions
 
 ;; These functions could reasonably be called by code in other packages.
diff --git a/ement-room.el b/ement-room.el
index 49b0d49326..a19df8dc02 100644
--- a/ement-room.el
+++ b/ement-room.el
@@ -152,6 +152,7 @@ Used to, e.g. call `ement-room-compose-org'.")
     (define-key map (kbd "r m") #'ement-list-members)
     (define-key map (kbd "r t") #'ement-room-set-topic)
     (define-key map (kbd "r f") #'ement-room-set-message-format)
+    (define-key map (kbd "r n") #'ement-room-set-notifications)
     (define-key map (kbd "r T") #'ement-tag-room)
 
     ;; Room membership
@@ -4148,6 +4149,7 @@ For use in `completion-at-point-functions'."
               ("r m" "List members" ement-list-members)
               ("r t" "Set topic" ement-room-set-topic)
               ("r f" "Set message format" ement-room-set-message-format)
+              ("r n" "Set notification rules" ement-room-set-notifications)
               ("r T" "Tag/untag room" ement-tag-room
                :description (lambda ()
                               (format "Tag/untag room (%s/%s)"

Reply via email to