Add interface for finding users in erc-server-PRIVMSG

* lisp/erc/erc-backend.el (erc-server-PRIVMSG): Call new hook
`erc--user-from-nick-function' for turning the sender's nick into a
channel user, if any.
* lisp/erc/erc-button.el (erc-button--add-phantom-speaker): Redo
completely using simplified API.
(erc-button--fallback-user-function): Add internal function-interface
variable for finding an `erc-server-user' object when the usual places
disappoint.
(erc-button--get-phantom-user): Add new function, a getter for
`erc-button--phantom-users'.
(erc-button--phantom-users-mode): Replace advice subscription for
`erc-button--modify-nick-function' with one for
`erc-button--user-from-nick-function' and one for
`erc-button--fallback-user-function'.
(erc-button--get-user-from-speaker-naive): Remove unused function.
(erc-button--add-nickname-buttons): Call
`erc-button--fallback-user-function' when a user can't be found in
`erc-server-users' or `erc-channel-users'.
* lisp/erc/erc.el (erc--user-from-nick-function): New
function-interface variable for determining an `erc-server-user'
`erc-channel-user' pair from the sender's nick.
(erc--examine-nick): Add new function to serve as default value for
`erc--user-from-nick-function'.  (Bug#60933)
This commit is contained in:
F. Jason Park 2023-04-28 06:34:09 -07:00
parent d141f7149b
commit ba44b48184
3 changed files with 54 additions and 39 deletions

View file

@ -102,6 +102,7 @@
(require 'erc-common)
(defvar erc--target)
(defvar erc--user-from-nick-function)
(defvar erc-channel-list)
(defvar erc-channel-users)
(defvar erc-default-nicks)
@ -1912,7 +1913,8 @@ add things to `%s' instead."
;; at this point.
(erc-update-channel-member (if privp nick tgt) nick nick
privp nil nil nil nil nil host login nil nil t)
(let ((cdata (erc-get-channel-user nick)))
(let ((cdata (funcall erc--user-from-nick-function
(erc-downcase nick) sndr parsed)))
(setq fnick (funcall erc-format-nick-function
(car cdata) (cdr cdata))))))
(cond

View file

@ -350,55 +350,56 @@ be updated at will.")
(defvar-local erc-button--phantom-users nil)
(defun erc-button--add-phantom-speaker (args)
"Maybe substitute fake `server-user' for speaker at point."
(pcase (car args)
((and obj (cl-struct erc-button--nick bounds downcased (user 'nil)))
;; Like `with-memoization' but don't cache when value is nil.
(when-let ((user (or (gethash downcased erc-button--phantom-users)
(erc-button--get-user-from-speaker-naive
(car bounds)))))
(cl-assert (null (erc-button--nick-data obj)))
(puthash downcased user erc-button--phantom-users)
(setf (erc-button--nick-data obj) (list (erc-server-user-nickname user))
(erc-button--nick-user obj) user))
(list obj))
(_ args)))
(defvar erc-button--fallback-user-function #'ignore
"Function to determine `erc-server-user' if not found in the usual places.
Called with DOWNCASED-NICK, NICK, and NICK-BOUNDS when
`erc-button-add-nickname-buttons' cannot find a user object for
DOWNCASED-NICK in `erc-channel-users' or `erc-server-users'.")
(defun erc-button--add-phantom-speaker (downcased nuh _parsed)
"Stash fictitious `erc-server-user' while processing \"PRIVMSG\".
Expect DOWNCASED to be the downcased nickname, NUH to be a triple
of (NICK LOGIN HOST), and parsed to be an `erc-response' object."
(pcase-let* ((`(,nick ,login ,host) nuh)
(user (or (gethash downcased erc-button--phantom-users)
(make-erc-server-user
:nickname nick
:host (and (not (string-empty-p host)) host)
:login (and (not (string-empty-p login)) login)))))
(list (puthash downcased user erc-button--phantom-users))))
(defun erc-button--get-phantom-user (down _word _bounds)
(gethash down erc-button--phantom-users))
;; In the future, we'll most likely create temporary
;; `erc-channel-users' tables during BATCH chathistory playback, thus
;; obviating the need for this mode entirely.
(define-minor-mode erc-button--phantom-users-mode
"Minor mode to recognize unknown speakers.
Expect to be used by module setup code for creating placeholder
users on the fly during history playback. Treat an unknown
PRIVMSG speaker, like <bob>, as if they were present in a 353 and
are thus a member of the channel. However, don't bother creating
an actual `erc-channel-user' object because their status prefix
is unknown. Instead, just spoof an `erc-server-user' by applying
early (outer), args-filtering advice wrapping
`erc-button--modify-nick-function'."
\"PRIVMSG\" speaker, like \"<bob>\", as if they previously
appeared in a prior \"353\" message and are thus a known member
of the channel. However, don't bother creating an actual
`erc-channel-user' object because their status prefix is unknown.
Instead, just spoof an `erc-server-user' and stash it during
\"PRIVMSG\" handling via `erc--user-from-nick-function' and
retrieve it during buttonizing via
`erc-button--fallback-user-function'."
:interactive nil
(if erc-button--phantom-users-mode
(progn
(add-function :filter-args (local 'erc-button--modify-nick-function)
#'erc-button--add-phantom-speaker '((depth . -90)))
(add-function :after-until (local 'erc--user-from-nick-function)
#'erc-button--add-phantom-speaker '((depth . -50)))
(add-function :after-until (local 'erc-button--fallback-user-function)
#'erc-button--get-phantom-user '((depth . 50)))
(setq erc-button--phantom-users (make-hash-table :test #'equal)))
(remove-function (local 'erc-button--modify-nick-function)
(remove-function (local 'erc--user-from-nick-function)
#'erc-button--add-phantom-speaker)
(remove-function (local 'erc-button--fallback-user-function)
#'erc-button--get-phantom-user)
(kill-local-variable 'erc-nicks--phantom-users)))
;; FIXME replace this after making ERC account-aware.
(defun erc-button--get-user-from-speaker-naive (point)
"Return `erc-server-user' object for nick at POINT."
(when-let*
(((eql ?< (char-before point)))
((eq (get-text-property point 'font-lock-face) 'erc-nick-default-face))
(parsed (erc-get-parsed-vector point)))
(pcase-let* ((`(,nick ,login ,host)
(erc-parse-user (erc-response.sender parsed))))
(make-erc-server-user
:nickname nick
:host (and (not (string-empty-p host)) host)
:login (and (not (string-empty-p login)) login)))))
(defun erc-button-add-nickname-buttons (entry)
"Search through the buffer for nicknames, and add buttons."
(let ((form (nth 2 entry))
@ -422,7 +423,9 @@ early (outer), args-filtering advice wrapping
(gethash down erc-channel-users)))
(user (or (and cuser (car cuser))
(and erc-server-users
(gethash down erc-server-users))))
(gethash down erc-server-users))
(funcall erc-button--fallback-user-function
down word bounds)))
(data (list word)))
(when (or (not (functionp form))
(and-let* ((user)

View file

@ -4993,6 +4993,16 @@ and as second argument the event parsed as a vector."
(and (erc-is-message-ctcp-p message)
(not (string-match "^\C-aACTION.*\C-a$" message))))
(defvar erc--user-from-nick-function #'erc--examine-nick
"Function to possibly consider unknown user.
Must return either nil or a cons of an `erc-server-user' and a
possibly nil `erc-channel-user' for formatting a server user's
nick. Called in the appropriate buffer with the downcased nick,
the parsed NUH, and the original `erc-response' object.")
(defun erc--examine-nick (downcased _nuh _parsed)
(and erc-channel-users (gethash downcased erc-channel-users)))
(defun erc-format-privmessage (nick msg privp msgp)
"Format a PRIVMSG in an insertable fashion."
(let* ((mark-s (if msgp (if privp "*" "<") "-"))