gomuks.el/gomuks.el
2026-05-09 12:23:18 +08:00

150 lines
5.7 KiB
EmacsLisp

;;; gomuks.el --- summary -*- lexical-binding: t -*-
;; This file is not part of GNU Emacs
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; commentary
;;; Code:
(cl-defstruct gomuks-state rooms spaces)
(cl-defstruct room id name topic events)
(cl-defstruct message row-id id sender body)
(defvar client-state (make-gomuks-state :rooms '() :spaces '()))
(defvar gomuks-server-buffer (get-buffer-create "*gomuks-server*"))
(defvar gomuks-socket-buffer "*gomuks-socket-frame*")
(defvar gomuks-server-name "*gomuks-server*")
(defvar gomuks-server-bin "~/Downloads/gomuks")
(defvar gomuks-server-proc
(start-process gomuks-server-name gomuks-server-buffer gomuks-server-bin))
(defvar gomuks-url "http://localhost:29325")
(defvar gomuks-auth-endpoint (concat gomuks-url "/_gomuks/auth"))
(defvar gomuks-auth-cookie )
(defvar gomuks-ws-endpoint (concat gomuks-url "/_gomuks/websocket"))
(defvar gomuks-auth-cookie nil)
(defvar gomuks-websocket nil)
(defvar gomuks-ping-timer nil)
(defun gomuks-connect (gomuks-username)
"Opens a websocket connection with the specified gomuks endpoint"
(interactive "sGomuks Username: \n")
(let ((gomuks-password (read-passwd "Gomuks Password:")))
(setq gomuks-auth-cookie (let ((request--curl-cookie-jar (expand-file-name (make-temp-name "gomuks-cookie-")
temporary-file-directory)))
(request gomuks-auth-endpoint
:type "POST"
:headers `(("Authorization" .
,(concat "Basic "
(base64-encode-string
(concat gomuks-username ":" gomuks-password))))))
(sleep-for 0.5)
(cdar (request--netscape-get-cookies request--curl-cookie-jar "localhost" "/_gomuks" t)))))
(setq gomuks-ping-timer (run-with-timer
0
15
(lambda ()
(message "pinging")
(websocket-send-text gomuks-websocket
"{\"command\":\"ping\"}"))))
(setq gomuks-websocket (websocket-open "ws://localhost:29325/_gomuks/websocket"
:custom-header-alist
`(("Cookie" . ,(concat "gomuks_auth=" gomuks-auth-cookie)))
:on-message (lambda (_ws frame)
(message "frame %s %s" (websocket-frame-completep frame) (websocket-frame-text
frame))
(cond
((not (websocket-frame-completep frame))
(message "frame incomplete")
(with-current-buffer (get-buffer-create gomuks-socket-buffer)
(goto-char (point-max))
(insert (websocket-frame-payload frame))))
(t
(message "frame complete")
(let* ((combined-payload
(unwind-protect
(with-current-buffer (get-buffer-create gomuks-socket-buffer)
(goto-char (point-max))
(insert (websocket-frame-payload frame))
(message "%s" (buffer-string))
(buffer-string))
(kill-buffer (get-buffer-create gomuks-socket-buffer))))
(msg (json-parse-string (decode-coding-string combined-payload 'utf-8)
:object-type 'alist
:array-type 'list
:null-object '())))
(message "test %S" msg)
(cond ((equal (alist-get 'command msg) "sync_complete")
(gomuks-sync-state (alist-get 'data msg)))))))))))
(defun gomuks-disconnect ()
"Disconnect the gomuks websocket."
(interactive)
(cancel-timer gomuks-ping-timer)
(websocket-close gomuks-websocket))
(defun gomuks-process-initial-events (events)
(mapcar
(lambda (event)
(cond
((and (or
(equal (alist-get 'type event) "m.room.message")
(equal (alist-get 'decrypted_type event) "m.room.message"))
(not (assoc 'relation_type event))) ;; Not an edit
(cons
(alist-get 'event_id event)
(make-message :row-id (alist-get 'rowid event)
:id (alist-get 'event_id event)
:sender (alist-get 'sender event)
:body (if (assoc 'decrypted event)
(alist-get 'body (alist-get 'decrypted event))
(alist-get 'body (alist-get 'content event))))))
(t
(cons (alist-get 'event_id event)
nil))))
events))
(defun gomuks-sync-state (data)
(let ((spaces (alist-get 'top_level_spaces data))
(rooms (alist-get 'rooms data)))
(when (< 0 (length spaces))
(setf (gomuks-state-spaces client-state) spaces))
(when (< 0 (length rooms))
(dolist (rm rooms)
(when (not (alist-get 'tombstone (alist-get 'meta (cdr rm))))
(if (not (alist-get (car rm) (gomuks-state-rooms client-state)))
(setf (gomuks-state-rooms client-state)
(cons (cons
(car rm)
(make-room :id (car rm)
:name (alist-get 'name (alist-get 'meta (cdr rm)))
:topic (alist-get 'topic (alist-get 'meta (cdr rm)))
:events (gomuks-process-initial-events (alist-get 'events (cdr rm)))))
(gomuks-state-rooms client-state)))
(let ((target-room (alist-get (car rm) (gomuks-state-rooms client-state))))
(setf (room-name target-room) (alist-get 'name (alist-get 'meta (cdr rm))))
(setf (room-topic target-room) (alist-get 'topic (alist-get 'meta (cdr rm))))
(setf (room-events target-room) (append (room-events target-room)
(gomuks-process-initial-events (alist-get 'events (cdr rm))))))))))))
(provide 'gomuks)
;;; gomuks.el ends here