feat: integrate SSE with message reception pipeline

This commit is contained in:
2026-06-11 12:49:29 -04:00
parent 55cab2fc28
commit 56501cba94
2 changed files with 25 additions and 83 deletions
+25 -41
View File
@@ -2,31 +2,22 @@
;;; Commentary:
;; Fetches new envelopes from signal-cli via the receive JSON-RPC method
;; Fetches new envelopes from signal-cli via SSE or JSON-RPC
;; and stores them in the local database: incoming messages, messages
;; sent from other linked devices (sync), delivery/read receipts, and
;; typing indicators. `noise-receive-start-polling' polls on a timer.
;; typing indicators. `noise-receive-start-sse' connects to the SSE
;; endpoint for real-time message reception.
;;; Code:
(require 'noise-rpc)
(require 'noise-db)
(require 'noise-conversation)
(require 'noise-sse)
(require 'cl-lib)
(declare-function noise-chat-list--render "noise-chat-list")
(defcustom noise-receive-interval 5
"Seconds between automatic polls of signal-cli for new messages."
:type 'integer
:group 'noise)
(defvar noise-receive--timer nil
"Active polling timer, or nil.")
(defvar noise-receive--last-poll-error nil
"Last error message shown by the poller, to avoid repeating it.")
;;;###autoload
(defun noise-receive ()
"Fetch and store new envelopes from signal-cli.
@@ -53,38 +44,31 @@ Returns the number of new messages stored."
(message "Received %d new message%s" new (if (= new 1) "" "s")))
new))
(defun noise-receive--handle-sse-event (event)
"Handle an SSE EVENT from signal-cli.
Processes the envelope and updates UI."
(noise-db-init)
(when-let ((envelope (noise-receive--field event "envelope")))
(when-let ((conv-id (noise-receive--process-envelope envelope)))
(noise-conversation-refresh-for conv-id)
(when-let ((buf (get-buffer "*signal*")))
(with-current-buffer buf
(noise-chat-list--render))))))
;; Override the default handler in noise-sse
(setq noise-sse--handle-event #'noise-receive--handle-sse-event)
;;;###autoload
(defun noise-receive-start-polling ()
"Start polling signal-cli for new messages every `noise-receive-interval'."
(defun noise-receive-start-sse ()
"Start receiving messages via SSE."
(interactive)
(noise-receive-stop-polling)
(setq noise-receive--timer
(run-with-timer 0 noise-receive-interval #'noise-receive--poll)))
(noise-sse-connect))
(defun noise-receive-stop-polling ()
"Stop the message polling timer."
;;;###autoload
(defun noise-receive-stop-sse ()
"Stop receiving messages via SSE."
(interactive)
(when noise-receive--timer
(cancel-timer noise-receive--timer)
(setq noise-receive--timer nil)))
(defun noise-receive--poll ()
"Poll for messages.
Errors are shown in the echo area once per distinct error rather than
on every poll, and never abort the timer."
(condition-case err
(progn
(noise-receive)
(setq noise-receive--last-poll-error nil))
(error
(let ((text (error-message-string err)))
(unless (equal text noise-receive--last-poll-error)
(setq noise-receive--last-poll-error text)
(message "Noise: receiving failed: %s%s"
text
(if (string-match-p "account parameter" text)
" — set `noise-account' or run M-x noise-select-account"
"")))))))
(noise-sse-disconnect))
;;; Envelope processing
-42
View File
@@ -1,42 +0,0 @@
;;; noise-receive-poll-test.el --- Tests for the receive poller -*- lexical-binding: t; -*-
(require 'ert)
(require 'noise-receive)
(ert-deftest noise-receive--poll-surfaces-error-once ()
"A failing poll messages the user once, not on every retry."
(let ((noise-receive--last-poll-error nil)
(messages nil))
(cl-letf (((symbol-function 'noise-receive)
(lambda ()
(signal 'noise-rpc-error
'("signal-cli error -32602: Method requires valid account parameter"))))
((symbol-function 'message)
(lambda (fmt &rest args)
(push (apply #'format fmt args) messages))))
(noise-receive--poll)
(noise-receive--poll))
(should (= 1 (length messages)))
(should (string-match-p "account parameter" (car messages)))
;; the hint points at the fix
(should (string-match-p "noise-select-account" (car messages)))))
(ert-deftest noise-receive--poll-recovers-and-resets ()
"After a successful poll, a recurring error is shown again."
(let ((noise-receive--last-poll-error nil)
(messages nil)
(fail t))
(cl-letf (((symbol-function 'noise-receive)
(lambda ()
(when fail
(signal 'noise-rpc-error '("connection refused")))
0))
((symbol-function 'message)
(lambda (fmt &rest args)
(push (apply #'format fmt args) messages))))
(noise-receive--poll) ; error -> message
(setq fail nil)
(noise-receive--poll) ; success -> resets
(setq fail t)
(noise-receive--poll)) ; error again -> message again
(should (= 2 (length messages)))))