Adding WS and managing the state better

This commit is contained in:
Miguel de la Cruz 2022-05-31 23:15:24 +02:00
parent 39e77a80e9
commit 23c1d56799

View file

@ -9,13 +9,20 @@
;;; Code:
(require 'websocket)
(defgroup mattermost nil
"Mattermost chat client"
:prefix "mattermost-"
:group 'applications)
(defcustom mattermost-server-url ""
"The URL of the Mattermost server"
(defcustom mattermost-login-id ""
"The username or email to log in the Mattermost server"
:group 'mattermost
:type 'string)
(defcustom mattermost-host ""
"The hostname of the Mattermost server"
:group 'mattermost
:type 'string)
@ -30,6 +37,9 @@
(defvar mattermost-user-id nil
"The ID of the authenticated user")
(defvar mattermost-websocket nil
"The websocket connected to the server")
;; ToDo: probably not the best way to get a keyword from a string
(defun mattermost-string->keyword (str)
"Returns a keyword from a string"
@ -40,9 +50,20 @@
(let ((json-object-type 'plist)
(json-array-type 'list)
(json-key-type 'keyword))
(with-current-buffer (get-buffer-create "asdf")
(insert (format "%s" (buffer-string))))
(goto-char url-http-end-of-headers)
(json-read)))
(defun mattermost-read-password ()
"Reads the password from the auth-source"
(let ((res (auth-source-search :max 1 :host mattermost-host :user mattermost-login-id)))
(if (and (listp res) (= (length res) 1))
(let ((secret (plist-get (car res) :secret)))
(if (functionp secret)
(funcall secret)
secret)))))
(defun mattermost-print-post (post)
"Prints the post in the current buffer"
(let* ((msg (plist-get post :message))
@ -51,7 +72,7 @@
(user-id (plist-get post :user_id))
(user (mattermost--get-user user-id))
(username (plist-get user :username)))
(insert (format "[%s] %s\n" username msg))))
(insert (format "[%s] %s\n" username (encode-coding-string msg 'utf-8)))))
(defun mattermost--get-user (user-id)
"Returns the user from the internal cache and fills the cache
@ -64,6 +85,25 @@ if the user is not present"
user)
user)))
(defun mattermost--get-auth-challenge ()
"Returns the JSON representation of the websocket authentication challenge"
(json-encode `(:seq 1
:action "authentication_challenge"
:data (:token ,mattermost-token))))
(defun mattermost--connect-websocket ()
"Returns a websocket configured to connect to the Mattermost
host and to dispatch and process the incoming messages"
(websocket-open (format "wss://%s/api/v4/websocket" mattermost-host)
:on-open (lambda (ws)
(websocket-send-text ws (mattermost--get-auth-challenge)))
:on-message (lambda (ws frame)
(let* ((text (websocket-frame-text frame))
(msg (json-parse-string text)))
(message "INCOMING> %s" msg)))
:on-close (lambda (ws)
(lwarn 'mattermost :error "websocket connection closed"))))
;; ToDo: once mattermost-request parses headers, use it to fetch both
;; the user (set the id to a local var) and the headers (set the
;; token)
@ -79,6 +119,7 @@ if the user is not present"
(response (mattermost-parse-json)))
(setq mattermost-token token)
(setq mattermost-user-id (plist-get response :id))
(mattermost--connect-websocket)
token))))
;; ToDo: update to parse headers as well
@ -103,7 +144,6 @@ if the user is not present"
(defun mattermost-get-channels (team-id)
"Returns the user's channel for a given team"
(let ((url (format "/users/%s/teams/%s/channels" mattermost-user-id team-id)))
(message "Channels url: %s" url)
(mattermost-request "GET" url)))
(defun mattermost-get-channel-messages (channel-id &optional page per-page)
@ -111,21 +151,6 @@ if the user is not present"
(let ((url (format "/channels/%s/posts" channel-id)))
(mattermost-request "GET" url)))
(with-current-buffer (get-buffer-create "*Mattermost Teams*")
(let ((teams (mattermost-get-teams)))
(erase-buffer)
(dolist (team teams)
(let* ((team-id (plist-get team :id))
(team-display-name (plist-get team :display_name))
(channels (mattermost-get-channels team-id)))
(insert (format "%s\n" team-display-name))
(dolist (channel channels)
(let ((channel-id (plist-get channel :id))
(channel-display-name (plist-get channel :display_name))
(channel-name (plist-get channel :name)))
(insert (format "> [%s] %s\n" channel-id (if (string= channel-display-name "")
channel-name
channel-display-name)))))))))
;; ToDo: define this better
(defvar mattermost-mode-map
(let ((map (make-sparse-keymap)))
@ -133,27 +158,50 @@ if the user is not present"
map)
"The keymap for mattermost-mode")
(define-derived-mode mattermost-mode fundamental-mode "Mattermost"
(defun show-root ()
(interactive)
(let ((rootb (get-buffer-create "*Mattermost Root*")))
(with-current-buffer rootb
(let ((teams (mattermost-get-teams)))
(erase-buffer)
(dolist (team teams)
(let* ((team-id (plist-get team :id))
(team-display-name (plist-get team :display_name))
(channels (mattermost-get-channels team-id)))
(insert (format "%s\n" team-display-name))
(dolist (channel channels)
(let ((channel-id (plist-get channel :id))
(channel-display-name (plist-get channel :display_name))
(channel-name (plist-get channel :name)))
(insert (format "> [%s] %s\n" channel-id (if (string= channel-display-name "")
channel-name
channel-display-name)))))))))
(set-buffer rootb)))
(define-derived-mode mattermost-root-mode fundamental-mode "Mattermost Root"
"Mode to list the Mattermost teams and channels to allow the
user to check their status and select between them")
(defun mattermost ()
"Connect to a Mattermost instance"
(interactive)
)
(if (string-empty-p mattermost-login-id)
;; ToDo: throw error here
(lwarn 'mattermost :error "`mattermost-login-id` cannot be empty"))
(if (string-empty-p mattermost-host)
;; ToDo: throw error here
(lwarn 'mattermost :error "`mattermost-host` cannot be empty"))
(mattermost-close)
(let ((password (mattermost-read-password)))
(mattermost-login mattermost-login-id password))
(show-root))
;; ToDo: remove
(setq msgs (mattermost-get-channel-messages "68w17u1da7yg7enayudjjqqwse"))
(car (plist-get msgs :posts))
(with-current-buffer (get-buffer-create "*Town Square*")
(erase-buffer)
(let ((posts (plist-get msgs :posts))
(order (plist-get msgs :order)))
(dolist (msgid order)
(let* ((msgid-keyword (mattermost-string->keyword msgid))
(post (plist-get posts msgid-keyword)))
(mattermost-print-post post)))))
(defun mattermost-close ()
"Closes the connection with the Mattermost server"
(unless (null mattermost-websocket)
(websocket-close mattermost-websocket))
(setq mattermost-token nil
mattermost-user-id nil))
(provide 'mattermost)
;;; mattermost.el ends here