Subject: 30.0.50; ERC 5.6-git: Treat erc-send-message more responsibly
From: "J.P." <[email-masked]>
To: [email-masked]
Date: Thu, 09 Nov 2023 18:26:51 -0800
List-Id: <debbugs-submit.debbugs.gnu.org>

Tags: patch

The function `erc-send-message', as used in-tree, sends and inserts
messages produced by handlers for certain slash commands. But rather
than being a simplistic analog for `erc-send-current-line', it's used in
practice to take over for that function in situations where the actual
prompt input is modified or replaced (and possibly reprocessed anew as
simulated "meta" input, as is the case with /SAY). From the user's
perspective, such messages are supposed to look and feel like all other
outgoing chat messages. However, at present, they don't.

I was originally hoping to introduce these changes for the next release,
but a bug has surfaced that exposes a major problem with the current
implementation.

From emacs -Q:

  1. Connect and join a channel
  2. Type /sm RET at the prompt
  3. Check the server buffer for a "message too long" error

The issue here is not that we've reached a module count large enough to
tip the outgoing text produced by `erc-modes' over the byte limit but
rather that `erc-send-message' has outgrown its usefulness and must now
evolve into something more robust and capable (in a backward compatible
way, of course).

In tackling this, I've opted to sidestep a few related concerns that
will have to be dealt with eventually. The first has to do with
discovering and possibly reintegrating whatever role the `noncommands'
module is designed to play in processing specialty slash commands, like
those reliant on `erc-send-message'. These days (reaching as far back as
Emacs 26.1), the module appears to effectively be a no-op, at least with
the default client configuration. Until we know more, I'm inclined to
let it rot away in erc-goodies, since its code only runs on interactive
input anyway.

Another issue I've avoided confronting with these changes is the ugly
relationship between `erc-format-privmessage', `erc-server-PRIVMSG',
`erc-format-nick-function', and `erc-display-message'. Sooner or later,
most of the speaker-related formatting shared between this bunch will
need to be consolidated and likely made accessible from
`erc-display-message' alone so that various next-gen features, such as
displaying messages from other devices sharing the same presence, can be
handled properly and made to look like the result of local prompt input.
All in good time, I guess.

In GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version
 3.24.38, cairo version 1.17.6) of 2023-11-09 built on localhost
Repository revision: 9c9b87639f919169eed956e9e7cce472d3a2f719
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12014000
System Description: Fedora Linux 37 (Workstation Edition)

Configured using:
 'configure --enable-check-lisp-object-type --enable-checking=yes,glyphs
 'CFLAGS=-O0 -g3'
 PKG_CONFIG_PATH=:/usr/lib64/pkgconfig:/usr/share/pkgconfig'

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG
JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 M17N_FLT MODULES NOTIFY
INOTIFY PDUMPER PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF
TOOLKIT_SCROLL_BARS WEBP X11 XDBE XIM XINPUT2 XPM GTK3 ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  value of $XMODIFIERS: @im=ibus
  locale-coding-system: utf-8-unix

Major mode: Lisp Interaction

Minor modes in effect:
  tooltip-mode: t
  global-eldoc-mode: t
  eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  minibuffer-regexp-mode: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
None found.

Features:
(shadow sort mail-extr emacsbug message mailcap yank-media puny dired
dired-loaddefs rfc822 mml mml-sec epa derived epg rfc6068 epg-config
gnus-util text-property-search time-date mm-decode mm-bodies mm-encode
mail-parse rfc2231 mailabbrev gmm-utils mailheader sendmail rfc2047
rfc2045 ietf-drums mm-util mail-prsvr mail-utils erc auth-source cl-seq
eieio eieio-core cl-macs password-cache json subr-x map format-spec
cl-loaddefs cl-lib erc-backend erc-networks byte-opt gv bytecomp
byte-compile erc-common erc-compat erc-loaddefs rmc iso-transl tooltip
cconv eldoc paren electric uniquify ediff-hook vc-hooks lisp-float-type
elisp-mode mwheel term/x-win x-win term/common-win x-dnd touch-screen
tool-bar dnd fontset image regexp-opt fringe tabulated-list replace
newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar
rfn-eshadow isearch easymenu timer select scroll-bar mouse jit-lock
font-lock syntax font-core term/tty-colors frame minibuffer nadvice seq
simple cl-generic indonesian philippine cham georgian utf-8-lang
misc-lang vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms
cp51932 hebrew greek romanian slovak czech european ethiopic indian
cyrillic chinese composite emoji-zwj charscript charprop case-table
epa-hook jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button
loaddefs theme-loaddefs faces cus-face macroexp files window
text-properties overlay sha1 md5 base64 format env code-pages mule
custom widget keymap hashtable-print-readable backquote threads dbusbind
inotify lcms2 dynamic-setting system-font-setting font-render-setting
cairo gtk x-toolkit xinput2 x multi-tty move-toolbar
make-network-process emacs)

Memory information:
((conses 16 66646 9545) (symbols 48 8720 0) (strings 32 23596 1638)
 (string-bytes 1 688941) (vectors 16 16134)
 (vector-slots 8 216395 14139) (floats 8 24 47) (intervals 56 242 0)
 (buffers 984 10))

From 2f5095294b7df8489fbb4c6ab9e75ad5fe1b2462 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Wed, 8 Nov 2023 19:14:55 -0800
Subject: [PATCH 1/5] [5.6] Make ERC error-notice formatting more consistent

* lisp/erc/erc-backend.el (erc--server-propagate-failed-connection):
Print leading notice prefix when reporting process exit status, and
set `erc-msg' type to `notice-error'.
(erc-schedule-reconnect): Print leading notice prefix when printing
`reconnecting' message.
* lisp/erc/erc.el (erc-process-input-line, erc-cmd-PART,
erc-cmd-TOPIC): Print `no-target' messages as error notices.
(erc-message-english-disconnected,
erc-message-english-disconnected-noreconnect): Hard-code standard
"***" notice prefix into message text, for consistency.
* test/lisp/erc/erc-tests.el (erc--refresh-prompt): Expect notice
prefix before `no-target' message.
---
 lisp/erc/erc-backend.el    |  4 ++--
 lisp/erc/erc.el            | 11 ++++++-----
 test/lisp/erc/erc-tests.el |  2 +-
 3 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index 4b5edaa77d2..32d891cd1c6 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -728,7 +728,7 @@ erc--server-propagate-failed-connection
 the server buffer in any case."
   (when (eq (process-status process) 'failed)
     (erc-display-message
-     nil 'error (process-buffer process)
+     nil '(notice error) (process-buffer process)
      (format "Process exit status: %S" (process-exit-status process)))
     (pcase (process-exit-status process)
       (111
@@ -995,7 +995,7 @@ erc-schedule-reconnect
                     (- erc-server-reconnect-attempts
                        (cl-incf erc-server-reconnect-count (or incr 1)))))
         (proc (buffer-local-value 'erc-server-process buffer)))
-    (erc-display-message nil 'error buffer 'reconnecting
+    (erc-display-message nil '(notice error) buffer 'reconnecting
                          ?m erc-server-reconnect-timeout
                          ?i (if count erc-server-reconnect-count "N")
                          ?n (if count erc-server-reconnect-attempts "A"))
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index fd57cb9d6a0..2d8f388328d 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -3729,7 +3729,7 @@ erc-process-input-line
       (let ((r (erc-default-target)))
         (if r
             (funcall erc-send-input-line-function r line force)
-          (erc-display-message nil 'error (current-buffer) 'no-target)
+          (erc-display-message nil '(notice error) (current-buffer) 'no-target)
           nil)))))
 
 (defconst erc--shell-parse-regexp
@@ -4581,7 +4581,7 @@ erc-cmd-PART
                                  (format "PART %s" ch)
                                (format "PART %s :%s" ch reason))
                              nil ch))
-        (erc-display-message nil 'error (current-buffer) 'no-target)))
+        (erc-display-message nil '(notice error) (current-buffer) 'no-target)))
     t)
    (t nil)))
 (put 'erc-cmd-PART 'do-not-parse-args t)
@@ -4921,7 +4921,7 @@ erc-cmd-TOPIC
           (progn
             (erc-log (format "cmd: TOPIC [%s]: %s" ch topic))
             (erc-server-send (format "TOPIC %s :%s" ch topic) nil ch))
-        (erc-display-message nil 'error (current-buffer) 'no-target)))
+        (erc-display-message nil '(notice error) (current-buffer) 'no-target)))
     t)
    (t nil)))
 (defalias 'erc-cmd-T #'erc-cmd-TOPIC)
@@ -8194,9 +8194,10 @@ erc-define-catalog
    (flood-ctcp-off . "FLOOD PROTECTION: Automatic CTCP responses turned off.")
    (flood-strict-mode
     . "FLOOD PROTECTION: Switched to Strict Flood Control mode.")
-   (disconnected . "\n\nConnection failed!  Re-establishing connection...\n")
+   (disconnected
+    . "\n\n*** Connection failed!  Re-establishing connection...\n")
    (disconnected-noreconnect
-    . "\n\nConnection failed!  Not re-establishing connection.\n")
+    . "\n\n*** Connection failed!  Not re-establishing connection.\n")
    (reconnecting . "Reconnecting in %ms: attempt %i/%n ...")
    (reconnect-canceled . "Canceled %u reconnect timer with %cs to go...")
    (finished . "\n\n*** ERC finished ***\n")
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 916b394c8ff..2898ca7be75 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -317,7 +317,7 @@ erc--refresh-prompt
         (insert "Howdy")
         (erc-send-current-line)
         (save-excursion (forward-line -1)
-                        (should (looking-at "No target"))
+                        (should (looking-at (rx "*** No target")))
                         (forward-line -1)
                         (should (looking-at "<tester> Howdy")))
         (should (looking-back "ServNet 6> "))
-- 
2.41.0

From a4a8360804d58beaa3357e35207d6f9bc7ef532b Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Fri, 15 Jul 2022 05:02:35 -0700
Subject: [PATCH 2/5] [5.6] Make ERC's message-sending functions more flexible

* lisp/erc/erc-backend.el (erc--server-send, erc-server-send): Convert
the latter to a wrapper that calls the former, a "new" internal
generic function.  Extend `no-penalty' parameter to mean ERC will
schedule an imminent send via a timer.  Always run
`erc-server-send-queue' on a timer.
* lisp/erc/erc.el (erc-send-action, erc--send-action): Make former a
wrapper for latter, a new generic function.  The rationale is that
built-in modules should be able to elect to handle insertion
themselves.  Also fix bug involving /ME ACTIONs not having
`erc-my-nick-face' applied to the speaker.
(erc-send-message, erc--send-message): Make the former a wrapper for
the latter, a new generic function.  Since commands, like /SV, use
this function to handle their own insertion, modules can't leverage
the normal API to suppress insertion.
(erc--send-input-lines): Convert to generic function.
---
 lisp/erc/erc-backend.el | 12 +++++++++---
 lisp/erc/erc.el         | 11 +++++++++--
 2 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index 32d891cd1c6..b16824b1f73 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -1171,7 +1171,7 @@ erc-server-send
 When FORCE is non-nil, bypass flood protection so that STRING is
 sent directly without modifying the queue.  When FORCE is the
 symbol `no-penalty', exempt this round from accumulating a
-timeout penalty.
+timeout penalty and schedule it to run ASAP instead of blocking.
 
 If TARGET is specified, look up encoding information for that
 channel in `erc-encoding-coding-alist' or
@@ -1179,6 +1179,9 @@ erc-server-send
 
 See `erc-server-flood-margin' for an explanation of the flood
 protection algorithm."
+  (erc--server-send string force target))
+
+(cl-defmethod erc--server-send (string force target)
   (erc-log (concat "erc-server-send: " string "(" (buffer-name) ")"))
   (setq erc-server-last-sent-time (erc-current-time))
   (let ((encoding (erc-coding-system-for-target target)))
@@ -1199,14 +1202,17 @@ erc-server-send
                         (when (fboundp 'set-process-coding-system)
                           (set-process-coding-system erc-server-process
                                                      'raw-text encoding))
-                        (process-send-string erc-server-process str))
+                        (if (and (eq force 'no-penalty))
+                            (run-at-time nil nil #'process-send-string
+                                         erc-server-process str)
+                          (process-send-string erc-server-process str)))
                     ;; See `erc-server-send-queue' for full
                     ;; explanation of why we need this condition-case
                     (error nil)))
               (setq erc-server-flood-queue
                     (append erc-server-flood-queue
                             (list (cons str encoding))))
-              (erc-server-send-queue (current-buffer))))
+              (run-at-time nil nil #'erc-server-send-queue (current-buffer))))
           t)
       (message "ERC: No process running")
       nil)))
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 2d8f388328d..a885038411d 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -2950,13 +2950,17 @@ erc-toggle-debug-irc-protocol
 
 (defun erc-send-action (tgt str &optional force)
   "Send CTCP ACTION information described by STR to TGT."
+  (erc--send-action tgt str force))
+
+(cl-defmethod erc--send-action (tgt str &optional force)
   (erc-send-ctcp-message tgt (format "ACTION %s" str) force)
   ;; Allow hooks that act on inserted PRIVMSG and NOTICES to process us.
   (let ((erc--msg-prop-overrides `((erc-msg . msg)
                                    (erc-ctcp . ACTION)
                                    ,@erc--msg-prop-overrides))
         (nick (erc-current-nick)))
-    (setq nick (propertize nick 'erc-speaker nick))
+    (setq nick (propertize nick 'erc-speaker nick
+                           'font-lock-face 'erc-my-nick-face))
     (erc-display-message nil '(t action input) (current-buffer)
                          'ACTION ?n nick ?a str ?u "" ?h "")))
 
@@ -4493,6 +4497,9 @@ erc-send-message
   "Send LINE to the current channel or user and display it.
 
 See also `erc-message' and `erc-display-line'."
+  (erc--send-message line force))
+
+(cl-defgeneric erc--send-message (line force)
   (erc-message "PRIVMSG" (concat (erc-default-target) " " line) force)
   (erc-display-line
    (concat (erc-format-my-nick) line)
@@ -6978,7 +6985,7 @@ erc--run-send-hooks
     (user-error "Multiline command detected" ))
   lines-obj)
 
-(defun erc--send-input-lines (lines-obj)
+(cl-defmethod erc--send-input-lines (lines-obj)
   "Send lines in `erc--input-split-lines' object LINES-OBJ."
   (when (erc--input-split-sendp lines-obj)
     (dolist (line (erc--input-split-lines lines-obj))
-- 
2.41.0

From ff74bec6e42aa9eede09f9361410f8bd14755027 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Fri, 9 Jun 2023 21:00:03 -0700
Subject: [PATCH 3/5] [5.6] Simplify logic for inserting ERC prompt input

* lisp/erc/erc.el (erc--current-line-input-split): New variable bound
to the current `erc--input-split' object, post review, for the extent
of display processing, so that "slash" commands can access it.
(erc-cmd-SAY): Defer to `erc--send-message'.
(erc--send-message): New implementation.  When called internally and
the variable `erc--current-line-input-split' is non-nil, treat input
as an outgoing message to ensure "send" hooks run and invariants for
prompt markers are preserved.  For example, this means noncommands,
like /SV will have `erc-input-face' applied, which is correct because
this message is sent to an actual target on the server.
(erc--make-input-split): New function.
(erc-send-current-line): Bind `erc--current-line-input-split' when
dispatching prompt-input handlers.  Use helper
`erc--make-input-split'.
(erc--run-send-hooks): Honor existing `refoldp' slot from
`erc--input-split' object.
---
 lisp/erc/erc.el | 50 +++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 40 insertions(+), 10 deletions(-)

diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index a885038411d..4828319727e 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -3659,6 +3659,12 @@ erc--called-as-input-p
   "Non-nil when a user types a \"/slash\" command.
 Remains bound until `erc-cmd-SLASH' returns.")
 
+(defvar erc--current-line-input-split nil
+  "Current `erc--input-split' instance when processing user input.
+This is for special cases in which a \"slash\" command needs
+details about the input it's handling or needs to detect whether
+it's been dispatched by `erc-send-current-line'.")
+
 (defvar-local erc-send-input-line-function #'erc-send-input-line
   "Function for sending lines lacking a leading \"slash\" command.
 When prompt input starts with a \"slash\" command, like \"/MSG\",
@@ -3795,9 +3801,7 @@ erc-cmd-SAY
   (if (string-match "^\\s-*$" line)
       nil
     (string-match "^ ?\\(.*\\)" line)
-    (let ((msg (match-string 1 line)))
-      (erc-display-msg msg)
-      (erc-process-input-line msg nil t))))
+    (erc--send-message (match-string 1 line) nil)))
 (put 'erc-cmd-SAY 'do-not-parse-args t)
 
 (defun erc-cmd-SET (line)
@@ -4507,6 +4511,27 @@ erc--send-message
   ;; FIXME - treat multiline, run hooks, or remove me?
   t)
 
+;; When running inside insert and send hooks, take more care in
+;; dealing with the prompt.
+(cl-defmethod erc--send-message
+  (input-line force &context (erc--current-line-input-split erc--input-split))
+  "Process non-command INPUT-LINE almost as if it's normal chat input.
+Expect INPUT-LINE to differ from the `string' slot of the calling
+context's `erc--current-line-input-split' object because the
+latter is likely a slash command invocation whose handler
+generated INPUT-LINE.  Before inserting INPUT-LINE, split it and
+run `erc-send-modify-hook' and `erc-send-post-hook' on each
+actual outgoing line.  Forgo input validation because this isn't
+interactive input, and skip `erc-send-completed-hook' because it
+will run just before the outer `erc-send-current-line' call
+returns."
+  (let* ((erc-flood-protect (not force))
+         (lines-obj (erc--make-input-split input-line)))
+    (setf (erc--input-split-refoldp lines-obj) t
+          (erc--input-split-cmdp lines-obj) nil)
+    (erc--send-input-lines (erc--run-send-hooks lines-obj)))
+  t)
+
 (defun erc-cmd-MODE (line)
   "Change or display the mode value of a channel or user.
 The first word specifies the target.  The rest is the mode string
@@ -6880,6 +6905,14 @@ erc--inhibit-slash-cmd-insertion
   (when (erc--input-split-cmdp state)
     (setf (erc--input-split-insertp state) nil)))
 
+(defun erc--make-input-split (string)
+  (make-erc--input-split
+   :string string
+   :insertp erc-insert-this
+   :sendp erc-send-this
+   :lines (split-string string erc--input-line-delim-regexp)
+   :cmdp (string-match erc-command-regexp string)))
+
 (defun erc-send-current-line ()
   "Parse current line and send it to IRC."
   (interactive)
@@ -6894,16 +6927,11 @@ erc-send-current-line
             (expand-abbrev))
           (widen)
           (let* ((str (erc-user-input))
-                 (state (make-erc--input-split
-                         :string str
-                         :insertp erc-insert-this
-                         :sendp erc-send-this
-                         :lines (split-string
-                                 str erc--input-line-delim-regexp)
-                         :cmdp (string-match erc-command-regexp str))))
+                 (state (erc--make-input-split str)))
             (run-hook-with-args 'erc--input-review-functions state)
             (when-let (((not (erc--input-split-abortp state)))
                        (inhibit-read-only t)
+                       (erc--current-line-input-split state)
                        (old-buf (current-buffer)))
               (let ((erc--msg-prop-overrides `((erc-msg . msg)
                                                ,@erc--msg-prop-overrides)))
@@ -6969,6 +6997,8 @@ erc--run-send-hooks
                       (run-hook-with-args 'erc-send-pre-hook str)
                       (make-erc-input :string str
                                       :insertp erc-insert-this
+                                      :refoldp (erc--input-split-refoldp
+                                                lines-obj)
                                       :sendp erc-send-this))))
         (run-hook-with-args 'erc-pre-send-functions state)
         (setf (erc--input-split-sendp lines-obj) (erc-input-sendp state)
-- 
2.41.0

From 52664b3d71a7bb3d6c57165300c9271d6079daaf Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Wed, 8 Nov 2023 21:20:54 -0800
Subject: [PATCH 4/5] [5.6] Slightly simplify text props on echoed ERC input

* lisp/erc/erc.el (erc--msg-props): Reformat doc to improve
readability.
(erc-send-current-line): Don't bind `erc--msg-prop-overrides' because
it affects all calls to `erc-display-message' made by slash commands,
etc.
(erc-display-msg): Set `erc-msg' property to `msg' instead of `self'
because the only legitimate use of this function is for inserting
"echoed" prompt input.  (Bug#60936)
---
 lisp/erc/erc.el | 39 +++++++++++++++++----------------------
 1 file changed, 17 insertions(+), 22 deletions(-)

diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 4828319727e..be8e2d19b7f 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -155,31 +155,28 @@ erc--msg-props
 as of ERC 5.6:
 
  - `erc-msg': a symbol, guaranteed present; values include:
-
-   - `msg', signifying a `PRIVMSG' or an incoming `NOTICE'
-   - `self', a fallback used by `erc-display-msg' for callers
-     that don't specify an `erc-msg'
-   - `unknown', a similar fallback for `erc-display-message'
-   - a catalog key, such as `s401' or `finished'
-   - an `erc-display-message' TYPE parameter, like `notice'
+   `msg', signifying a `PRIVMSG' or an incoming `NOTICE';
+   `unknown', a fallback for `erc-display-message'; a catalog
+    key, such as `s401' or `finished'; an `erc-display-message'
+    TYPE parameter, like `notice'
 
  - `erc-cmd': a message's associated IRC command, as read by
    `erc--get-eq-comparable-cmd'; currently either a symbol, like
    `PRIVMSG', or a number, like 5, which represents the numeric
-   \"005\"; absent on \"local\" messages, such as simple warnings
-   and help text, and on outgoing messages unless echoed back by
-   the server (assuming future support)
+    \"005\"; absent on \"local\" messages, such as simple warnings
+    and help text, and on outgoing messages unless echoed back by
+    the server (assuming future support)
 
  - `erc-ctcp': a CTCP command, like `ACTION'
 
  - `erc-ts': a timestamp, possibly provided by the server; as of
-   5.6, a ticks/hertz pair on Emacs 29 and above, and a \"list\"
-   type otherwise; managed by the `stamp' module
+    5.6, a ticks/hertz pair on Emacs 29 and above, and a \"list\"
+    type otherwise; managed by the `stamp' module
 
  - `erc-ephemeral': a symbol prefixed by or matching a module
-   name; indicates to other modules and members of modification
-   hooks that the current message should not affect stateful
-   operations, such as recording a channel's most recent speaker
+    name; indicates to other modules and members of modification
+    hooks that the current message should not affect stateful
+    operations, such as recording a channel's most recent speaker
 
 This is an internal API, and the selection of related helper
 utilities is fluid and provisional.  As of ERC 5.6, see the
@@ -6933,8 +6930,7 @@ erc-send-current-line
                        (inhibit-read-only t)
                        (erc--current-line-input-split state)
                        (old-buf (current-buffer)))
-              (let ((erc--msg-prop-overrides `((erc-msg . msg)
-                                               ,@erc--msg-prop-overrides)))
+              (progn ; unprogn this during next major surgery
                 (erc-set-active-buffer (current-buffer))
                 ;; Kill the input and the prompt
                 (delete-region erc-input-marker (erc-end-of-input-line))
@@ -7079,16 +7075,15 @@ erc-send-input
 
 (defun erc-display-msg (line)
   "Insert LINE into current buffer and run \"send\" hooks.
-Expect LINE to originate from input submitted interactively at
-the prompt, such as outgoing chat messages or echoed slash
-commands."
+Treat LINE as input submitted interactively at the prompt, such
+as outgoing chat messages and echoed slash commands."
   (when erc-insert-this
     (save-excursion
       (erc--assert-input-bounds)
       (let ((insert-position (marker-position (goto-char erc-insert-marker)))
-            (erc--msg-props (or erc--msg-props ; prefer `self' to `unknown'
+            (erc--msg-props (or erc--msg-props
                                 (let ((ovs erc--msg-prop-overrides))
-                                  (map-into `((erc-msg . self) ,@(reverse ovs))
+                                  (map-into `((erc-msg . msg) ,@(reverse ovs))
                                             'hash-table))))
             beg)
         (insert (erc-format-my-nick))
-- 
2.41.0

From 667ef74338ef3d085db96eb9f4cecccd984d43a3 Mon Sep 17 00:00:00 2001
From: "F. Jason Park" <[email-masked]>
Date: Thu, 9 Nov 2023 10:37:49 -0800
Subject: [PATCH 5/5] [5.6] Don't output non-modules in erc-modes

* lisp/erc/erc.el (erc-modes): Only list actual modules created via
`define-erc-module.  Also `quote' members of the resulting list.
---
 lisp/erc/erc.el | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index be8e2d19b7f..00ee97b7e14 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -8135,8 +8135,9 @@ erc-modes
                     (let (modes (case-fold-search nil))
                       (dolist (var (apropos-internal "^erc-.*mode$"))
                         (when (and (boundp var)
+                                   (get var 'erc-module)
                                    (symbol-value var))
-                          (setq modes (cons (symbol-name var)
+                          (setq modes (cons (concat "`" (symbol-name var) "'")
                                             modes))))
                       modes)
                     ", ")))
-- 
2.41.0