From: Philip Kaludercic Date: Mon, 19 Sep 2022 19:15:04 +0000 (+0200) Subject: Have rcirc handle bridge bots X-Git-Tag: emacs-29.0.90~1856^2~349 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=1d9a8884db63d430c96ce53e3d24c278dd8dbd8c;p=emacs.git Have rcirc handle bridge bots * doc/misc/rcirc.texi (Dealing with Bridge Bots): Document new feature. * etc/NEWS: Mention the new feature. * lisp/net/rcirc.el (rcirc-markup-text-functions): Add new markup function (rcirc-pseudo-nicks): Add new local variable. (rcirc-channel-nicks): Use 'rcirc-pseudo-nicks' for nick completion. (rcirc-bridge-bot-alist): Add new user option. (rcirc-bridged-nick): Add new face. (rcirc-markup-bridge-bots): Add new function. --- diff --git a/doc/misc/rcirc.texi b/doc/misc/rcirc.texi index 8c798d6c33b..a028697847c 100644 --- a/doc/misc/rcirc.texi +++ b/doc/misc/rcirc.texi @@ -859,6 +859,7 @@ Here are some examples of stuff you can do to configure @code{rcirc}. * Changing the time stamp format:: * Defining a new command:: * Using rcirc with bouncers:: +* Dealing with Bridge Bots:: @end menu @node Skipping /away messages using handlers @@ -992,6 +993,46 @@ displayed. A simple configuration to fix the above example might be: rcirc-channel-filter #'local/rcirc-soju-suffix) @end smallexample +@node Dealing with Bridge Bots +@section Dealing with Bridge Bots +@cindex bridge + +It is increasingly common for IRC channels to be ``bridged'' onto +other networks such as XMPP, Matrix, etc. Sometimes the software does +a good job at mapping each non-IRC user into an IRC user, but more +often than not it doesn't. In that case you might receive a message +like: + +@example +@verbatim +09:47 I am not on IRC +@end verbatim +@end example + +where @samp{bridge} is a bot responsible for sending messages back and +forth between networks, and @samp{john} is the user name of someone on +a different network. Note that the bot indicates this within the +message (@verb{| I am not on IRC|}) that appears in your chat +buffer. + +@vindex rcirc-bridge-bot-alist +If this annoys you, the user option @code{rcirc-bridge-bot-alist} may +be of use. It consists of descriptions of what users are these kinds +of ``bridge bots'' and how they format their messages. To handle the +above example, we might set the user option to: + +@example +(setopt rcirc-bridge-bot-alist + '(("bridge" . "<\\(.+?\\)>[[:space:]]+"))) +@end example + +If there is an entry for the current user, @code{rcirc} will take the +associated regular expression and try to find a match in the message +string. If it manages to find anything, the matching expression is +deleted from the message. The regular expression must contain at +least one group that will match the user name of the bridged message. +This will then be used to replace the username of the bridge bot. + @node GNU Free Documentation License @appendix GNU Free Documentation License @include doclicense.texi diff --git a/etc/NEWS b/etc/NEWS index 9b4238eea70..5e34f43179f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1060,6 +1060,22 @@ Rcirc will use the default 'completion-at-point' mechanism. The conventional IRC behavior of completing by cycling through the available options can be restored by enabling this option. ++++ +*** New user option 'rcirc-bridge-bot-alist'. +If you are in a channel where a bot is responsible for bridging +between networks, you can use this variable to make these messages +appear more native. For example you might set the option to: + + (setq rcirc-bridge-bot-alist '(("bridge" . "{\\(.+?\\)}[[:space:]]+"))) + +for messages like + + 09:47 {john} I am not on IRC + +to be reformatted into + + 09:47 I am not on IRC + ** Imenu +++ diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el index abb67da95f0..5e48b3c70f2 100644 --- a/lisp/net/rcirc.el +++ b/lisp/net/rcirc.el @@ -1925,7 +1925,8 @@ PROCESS is the process object for the current connection." rcirc-markup-my-nick rcirc-markup-urls rcirc-markup-keywords - rcirc-markup-bright-nicks) + rcirc-markup-bright-nicks + rcirc-markup-bridge-bots) "List of functions used to manipulate text before it is printed. Each function takes two arguments, SENDER, and RESPONSE. The @@ -2220,24 +2221,27 @@ PROCESS is the process object for the current connection." (puthash nick newchans rcirc-nick-table) (remhash nick rcirc-nick-table))))) +(defvar rcirc-pseudo-nicks) (defun rcirc-channel-nicks (process target) "Return the list of nicks associated with TARGET sorted by last activity. PROCESS is the process object for the current connection." (when target (if (rcirc-channel-p target) - (with-rcirc-process-buffer process - (let (nicks) - (maphash - (lambda (k v) - (let ((record (assoc-string target v t))) - (if record - (setq nicks (cons (cons k (cdr record)) nicks))))) - rcirc-nick-table) - (mapcar (lambda (x) (car x)) - (sort nicks (lambda (x y) - (let ((lx (or (cdr x) 0)) - (ly (or (cdr y) 0))) - (< ly lx))))))) + (let ((pseudo-nicks (mapcar #'list rcirc-pseudo-nicks))) + (with-rcirc-process-buffer process + (let (nicks) + (maphash + (lambda (k v) + (let ((record (assoc-string target v t))) + (if record + (setq nicks (cons (cons k (cdr record)) nicks))))) + rcirc-nick-table) + (mapcar (lambda (x) (car x)) + (sort (nconc pseudo-nicks nicks) + (lambda (x y) + (let ((lx (or (cdr x) 0)) + (ly (or (cdr y) 0))) + (< ly lx)))))))) (list target)))) (defun rcirc-ignore-update-automatic (nick) @@ -2911,6 +2915,78 @@ If ARG is given, opens the URL in a new browser window." (insert (rcirc-facify (format-time-string rcirc-time-format time) 'rcirc-timestamp)))) +(defvar-local rcirc-pseudo-nicks '() + "List of virtual nicks detected in a channel. +These are collected by `rcirc-markup-bridge-bots' and don't +constitute actual users in the current channel. Usually these +are bridged via a some bot as described in +`rcirc-bridge-bot-alist'.") + +(defcustom rcirc-bridge-bot-alist '() + "Alist for handling bouncers by `rcirc-markup-bridge-bots'. +Each entry has the form (NAME . REGEXP), where NAME is the user +name of a bouncer and REGEXP is a pattern used to match the +message. The matching part of the message will be stripped from +the message, and the first match group will replace the user name +of the bot. Any matched name will noted and used in some cases +for nick completion." + :type '(alist :key-type (string :tag "Bot name") + :value-type regexp) + :version "29.1") + +(defface rcirc-bridged-nick + '((((class color) (min-colors 88) (background light)) :background "SlateGray1") + (((class color) (min-colors 88) (background dark)) :background "DarkSlateGray4") + (((class color) (min-colors 16) (background light)) :background "LightBlue") + (((class color) (min-colors 16) (background dark)) :background "DarkSlateGray") + (t :background "blue")) + "Face used for pseudo-nick ." + :version "29.1") + +(defun rcirc-markup-bridge-bots (sender response) + "Detect and reformat bridged messages to appear more natural. +The user option `rcirc-bridge-bot-alist' specified what SENDER to +handle. This function only operates on direct messages (as +indicated by RESPONSE)." + (catch 'quit + (atomic-change-group + (save-match-data + (when-let* (((string= response "PRIVMSG")) + (regexp (alist-get sender rcirc-bridge-bot-alist + nil nil #'string=)) + ((search-forward-regexp regexp nil t)) + (nick (match-string-no-properties 1))) + (replace-match "") ;delete the bot string + (unless (member nick rcirc-pseudo-nicks) + (push nick rcirc-pseudo-nicks)) + (goto-char (point-min)) + (let ((fmt (alist-get "PRIVMSG" rcirc-response-formats + nil nil #'string=)) + (hl-face (cond ((member sender rcirc-bright-nicks) + 'rcirc-bright-nick) + ((member sender rcirc-dim-nicks) + 'rcirc-dim-nick) + (t 'rcirc-other-nick))) + hl-username-p) + (when (string-match (rx (* "%%") "%" (group (or ?N ?n))) fmt) + (when (string= (match-string 1 fmt) "N") + (setq hl-username-p t)) + (search-forward-regexp + (format-spec + (alist-get "PRIVMSG" rcirc-response-formats + nil nil #'string=) + `((?m . "") (?r . "") (?t . "") (?f . "") + (?N . ,(rx (group (+? nonl)))) + (?n . ,(rx (group (+? nonl)))))) + nil t) + (replace-match + (propertize + nick + 'help-echo (format "Message bridged via %s" sender) + 'face `(,@(and hl-username-p (list hl-face)) + rcirc-bridged-nick)) + nil t nil 1)))))))) + (defun rcirc-markup-attributes (_sender _response) "Highlight IRC markup, indicated by ASCII control codes." (while (re-search-forward