Merge branch 'feature/rcirc-update'

This commit is contained in:
Philip Kaludercic 2021-09-15 17:36:19 +02:00
commit 7a69fe3bc9
No known key found for this signature in database
GPG key ID: 181F5B95AE30B336
3 changed files with 396 additions and 161 deletions

View file

@ -254,6 +254,10 @@ To make this permanent, add the following to your init file:
Use @kbd{C-c C-@key{SPC}} to switch to these buffers.
@vindex rcirc-track-ignore-server-buffer-flag
If the user wishes to ignore events in the server buffer, set
@code{rcirc-track-ignore-server-buffer-flag} to a non-nil value.
@node Reference
@chapter Reference
@cindex reference
@ -426,7 +430,13 @@ lost. The simple solution is to use @kbd{M-x rcirc}. The problem is
that this opens an @emph{additional} connection, so you'll have two
copies of every channel buffer, one dead and one live.
The real answer, therefore, is the @code{/reconnect} command.
One option therefore, is the @code{/reconnect} command.
An other approach is to set @code{rcirc-reconnect-delay} to a value
greater than 0, and allow rcirc to reconnect when it detects that the
connection has been closed. By default it will try to do this three
times (as specified by @code{rcirc-reconnect-attempts}), before giving
up.
@end table
@node Useful IRC commands
@ -671,6 +681,12 @@ window is showing them), the mode line will now show you the abbreviated
channel or nick name. Use @kbd{C-c C-@key{SPC}} to switch to these
buffers.
@cindex rcirc-track-abbrevate-flag
By default the channel names are abbreviated, set
@code{rcirc-track-abbrevate-flag} to a non-nil value. This might be
interesting if the IRC activities are not tracked in the mode line,
but somewhere else.
@vindex rcirc-mode-hook
If you prefer not to load @code{rcirc} immediately, you can delay the
activation of this mode:
@ -807,6 +823,18 @@ active and only omits a message if the nick has not been active. The
window @code{rcirc} considers is controlled by the
@code{rcirc-omit-threshold} variable.
@vindex rcirc-omit-responses-after-join
Right after connecting to a server, rcirc will also hide all messages
in @code{rcirc-omit-responses-after-join}, next to
@code{rcirc-omit-responses}. For example,
@example
(setq rcirc-omit-responses-after-join '("TOPIC" "NICK"))
@end example
would hide the topic message and the list of users in the current
channel right after joining a new channel.
@node Hacking and Tweaking
@chapter Hacking and Tweaking
@cindex hacking and tweaking
@ -819,6 +847,7 @@ Here are some examples of stuff you can do to configure @code{rcirc}.
* Scrolling conservatively::
* Changing the time stamp format::
* Defining a new command::
* Using rcirc with bouncers::
@end menu
@node Skipping /away messages using handlers
@ -903,22 +932,53 @@ how to include the date in the time stamp:
@cindex new commands, defining
Here's a simple new command, @code{/sv}. With it, you can boast about
your IRC client. It shows how you can use @code{defun-rcirc-command} to
your IRC client. It shows how you can use @code{rcirc-define-command} to
define new commands.
@findex rcirc-define-command
We're waiting for the definition of this command until @code{rcirc} is loaded
because @code{defun-rcirc-command} is not yet available, and without
because @code{rcirc-define-command} is not yet available, and without
@code{rcirc} loaded, the command wouldn't do us much good anyway.
@smallexample
(with-eval-after-load 'rcirc
(defun-rcirc-command sv (arg)
(rcirc-define-command sv ()
"Boast about rcirc."
(interactive "i")
(rcirc-send-message process target
(concat "I use " rcirc-id-string))))
(rcirc-send-message process target "I use " rcirc-id-string)))
@end smallexample
@node Using rcirc with bouncers
@section Using rcirc with bouncers
@cindex bouncer
Some bouncers multiplex connections to various servers, but have to
modify nicks and channel names to make this work. The channel
@code{#emacs} on @code{irc.libera.chat} becomes
@code{#emacs/irc.libera.chat}.
@vindex rcirc-nick-filter
@vindex rcirc-channel-filter
The options @code{rcirc-nick-filter} and @code{rcirc-channel-filter}
can be used to make this feel more natural. When set to functions,
these will be used to change how nicks and channel names are
displayed. A simple configuration to fix the above example might be:
@smallexample
(defun my/rcirc-remove-suffix (STR)
"Remove suffixes from STR."
(save-match-data
(if (string-match "/[[:alpha:]]+?\\'" str)
(substring str 0 (match-beginning 0))
str)))
(setq rcirc-nick-filter #'my/rcirc-remove-suffix
rcirc-channel-filter #'local/rcirc-soju-suffix)
@end smallexample
The effect is that buffer names, nicks in messages, nick-completion
all strip away the suffix introduced by the bouncer.
@node GNU Free Documentation License
@appendix GNU Free Documentation License
@include doclicense.texi

View file

@ -2746,6 +2746,44 @@ Variable 'mh-whitelist-preserves-sequences-flag' is renamed
Face 'mh-folder-blacklisted' is renamed 'mh-folder-blocklisted'.
Face 'mh-folder-whitelisted' is renamed 'mh-folder-allowlisted'.
** Rcirc
+++
*** rcirc now supports SASL authentication.
---
*** rcirc connects asynchronously
---
*** Integrate formatting into rcirc-send-string
The function now accepts a variable number of arguments.
+++
*** Deprecate defun-rcirc-command in favour of rcirc-define-command
The new macro handles multiple and optional arguments.
---
*** Add basic IRCv3 support
This includes support for the capabilities: server-time, batch,
message-ids, invite-notify, multi-prefix and standard-replies.
---
*** Add mouse property support to rcirc-track-minor-mode
---
*** Improve support for IRC markup codes
---
*** Check auth-sources for server passwords
---
*** Allow for channels to hide certain message types right after connecting.
Set rcirc-omit-responses-after-join analogously to rcirc-omit-responses.
+++
*** Implement repeated reconnection strategy
See rcirc-reconnect-attempts.
** Miscellaneous
---
@ -2766,9 +2804,6 @@ will now restore the original order.
---
*** 'M-left' and 'M-right' now move between columns in 'tabulated-list-mode'.
+++
*** rcirc now supports SASL authentication.
---
*** New variable 'hl-line-overlay-priority'.
This can be used to change the priority of the hl-line overlays.

View file

@ -60,9 +60,9 @@
(defcustom rcirc-server-alist
(if (gnutls-available-p)
'(("irc.libera.chat" :channels ("#rcirc")
'(("irc.libera.chat" :channels ("#emacs" "#rcirc")
:port 6697 :encryption tls))
'(("irc.libera.chat" :channels ("#rcirc"))))
'(("irc.libera.chat" :channels ("#emacs" "#rcirc"))))
"An alist of IRC connections to establish when running `rcirc'.
Each element looks like (SERVER-NAME PARAMETERS).
@ -194,16 +194,15 @@ If nil, no maximum is applied."
"Responses which will be hidden when `rcirc-omit-mode' is enabled."
:type '(repeat string))
(defcustom rcirc-omit-after-reconnect
'("JOIN" "TOPIC" "NAMES")
"Types of messages to hide right after reconnecting."
(defcustom rcirc-omit-responses-after-join '()
"Types of messages to hide right after joining a channel."
:type '(repeat string)
:version "28.1")
(defvar-local rcirc-reconncting nil
"Non-nil means we have just reconnected.
(defvar-local rcirc-joined nil
"Non-nil means we have just connected.
This is used to hide the message types enumerated in
`rcirc-supress-after-reconnect'.")
`rcirc-omit-responses-after-join'.")
(defvar-local rcirc-prompt-start-marker nil
"Marker indicating the beginning of the message prompt.")
@ -215,11 +214,8 @@ Uninteresting lines are those whose responses are listed in
`rcirc-omit-responses'."
:lighter " Omit"
(if rcirc-omit-mode
(progn
(add-to-invisibility-spec '(rcirc-omit . nil))
(message "Rcirc-Omit mode enabled"))
(remove-from-invisibility-spec '(rcirc-omit . nil))
(message "Rcirc-Omit mode disabled"))
(add-to-invisibility-spec '(rcirc-omit . nil))
(remove-from-invisibility-spec '(rcirc-omit . nil)))
(dolist (window (get-buffer-window-list (current-buffer)))
(with-selected-window window
(recenter (when (> (point) rcirc-prompt-start-marker) -1)))))
@ -413,6 +409,21 @@ will be killed."
:version "28.1"
:type 'function)
(defcustom rcirc-channel-filter #'identity
"Function applied to channels before displaying."
:version "28.1"
:type 'function)
(defcustom rcirc-track-ignore-server-buffer-flag nil
"Non-nil means activities in the server buffer are not traced."
:version "28.1"
:type 'boolean)
(defcustom rcirc-display-server-buffer t
"Non-nil means the server buffer should be shown on connecting."
:version "28.1"
:type 'boolean)
(defvar-local rcirc-nick nil
"The nickname used for the current connection.")
@ -512,10 +523,12 @@ If ARG is non-nil, instead prompt for connection parameters."
:channels)
" "))
"[, ]+" t))
(encryption (rcirc-prompt-for-encryption server-plist)))
(rcirc-connect server port nick user-name
rcirc-default-full-name
channels password encryption))
(encryption (rcirc-prompt-for-encryption server-plist))
(process (rcirc-connect server port nick user-name
rcirc-default-full-name
channels password encryption)))
(when rcirc-display-server-buffer
(pop-to-buffer-same-window (process-buffer process))))
;; connect to servers in `rcirc-server-alist'
(let (connected-servers)
(dolist (c rcirc-server-alist)
@ -544,9 +557,11 @@ If ARG is non-nil, instead prompt for connection parameters."
(setq connected p)))
(if (not connected)
(condition-case nil
(rcirc-connect server port nick user-name
full-name channels password encryption
server-alias)
(let ((process (rcirc-connect server port nick user-name
full-name channels password encryption
server-alias)))
(when rcirc-display-server-buffer
(pop-to-buffer-same-window (process-buffer process))))
(quit (message "Quit connecting to %s"
(or server-alias server))))
(with-current-buffer (process-buffer connected)
@ -595,6 +610,8 @@ FULL-NAME STARTUP-CHANNELS PASSWORD ENCRYPTION SERVER-ALIAS).
See `rcirc-connect' for more details on these variables.")
(defvar-local rcirc-process nil
"Network process for the current connection.")
(defvar-local rcirc-last-connect-time nil
"The last time the buffer was connected.")
;;; IRCv3 capability negotiation (https://ircv3.net/specs/extensions/capability-negotiation)
(defvar rcirc-implemented-capabilities
@ -604,6 +621,16 @@ See `rcirc-connect' for more details on these variables.")
"message-ids" ;https://ircv3.net/specs/extensions/message-ids
"invite-notify" ;https://ircv3.net/specs/extensions/invite-notify
"sasl" ;https://ircv3.net/specs/extensions/sasl-3.1
"multi-prefix" ;https://ircv3.net/specs/extensions/multi-prefix
"standard-replies" ;https://ircv3.net/specs/extensions/standard-replies
;; The following capabilities should be implemented as soon as
;; their specifications are undrafted:
;;
;; "reply" ;https://ircv3.net/specs/client-tags/reply
;; "react" ;https://ircv3.net/specs/client-tags/react
;; "multiline" ;https://ircv3.net/specs/extensions/multiline
;; "chathistory" ;https://ircv3.net/specs/extensions/chathistory
;; "channel-rename" ;https://ircv3.net/specs/extensions/channel-rename
)
"A list of capabilities that rcirc supports.")
(defvar-local rcirc-requested-capabilities nil
@ -611,7 +638,7 @@ See `rcirc-connect' for more details on these variables.")
(defvar-local rcirc-acked-capabilities nil
"A list of capabilities that the server supports.")
(defvar-local rcirc-finished-sasl t
"Check whether SASL authentication has completed")
"Check whether SASL authentication has completed.")
(defun rcirc-get-server-method (server)
"Return authentication method for SERVER."
@ -644,69 +671,61 @@ that are joined after authentication."
(message "Connecting to %s..." (or server-alias server))
(let* ((inhibit-eol-conversion)
(port-number (if port
(if (stringp port)
(string-to-number port)
port)
rcirc-default-port))
(nick (or nick rcirc-default-nick))
(user-name (or user-name rcirc-default-user-name))
(full-name (or full-name rcirc-default-full-name))
(startup-channels startup-channels)
(use-sasl (eq (rcirc-get-server-method server) 'sasl))
(process (open-network-stream
(if (stringp port)
(string-to-number port)
port)
rcirc-default-port))
(nick (or nick rcirc-default-nick))
(user-name (or user-name rcirc-default-user-name))
(full-name (or full-name rcirc-default-full-name))
(startup-channels startup-channels)
process)
;; Ensure any previous process is killed
(when-let ((old-process (get-process (or server-alias server))))
(set-process-sentinel old-process #'ignore)
(delete-process process))
;; Set up process
(setq process (open-network-stream
(or server-alias server) nil server port-number
:type (or encryption 'plain))))
;; set up process
:type (or encryption 'plain)
:nowait t))
(set-process-coding-system process 'raw-text 'raw-text)
(switch-to-buffer (rcirc-generate-new-buffer-name process nil))
(set-process-buffer process (current-buffer))
(unless (eq major-mode 'rcirc-mode)
(rcirc-mode process nil))
(set-process-sentinel process 'rcirc-sentinel)
(set-process-filter process 'rcirc-filter)
(with-current-buffer (get-buffer-create (rcirc-generate-new-buffer-name process nil))
(set-process-buffer process (current-buffer))
(unless (eq major-mode 'rcirc-mode)
(rcirc-mode process nil))
(set-process-sentinel process #'rcirc-sentinel)
(set-process-filter process #'rcirc-filter)
(setq rcirc-connection-info
(list server port nick user-name full-name startup-channels
password encryption server-alias))
(setq rcirc-process process)
(setq rcirc-server server)
(setq rcirc-server-name (or server-alias server)) ; Update when we get 001 response.
(setq rcirc-nick-table (make-hash-table :test 'equal))
(setq rcirc-nick nick)
(setq rcirc-startup-channels startup-channels)
(setq rcirc-last-server-message-time (current-time))
(setq rcirc-connection-info
(list server port nick user-name full-name startup-channels
password encryption server-alias))
(setq rcirc-process process)
(setq rcirc-server server)
(setq rcirc-server-name (or server-alias server)) ; Update when we get 001 response.
(setq rcirc-nick-table (make-hash-table :test 'equal))
(setq rcirc-nick nick)
(setq rcirc-startup-channels startup-channels)
(setq rcirc-last-server-message-time (current-time))
(setq rcirc-last-connect-time (current-time))
(setq rcirc-connecting t)
;; Check if the immediate process state
(sit-for .1)
(cond
((eq (process-status process) 'failed)
(setq mode-line-process ":disconnected")
(setq rcirc-connecting nil))
((eq (process-status process) 'connect)
(setq mode-line-process ":connecting")
(setq rcirc-connecting t)))
(add-hook 'auto-save-hook 'rcirc-log-write)
(when use-sasl
(rcirc-send-string process "CAP REQ sasl"))
(add-hook 'auto-save-hook #'rcirc-log-write)
(when use-sasl
(setq-local rcirc-finished-sasl nil))
;; identify
(dolist (cap rcirc-implemented-capabilities)
(rcirc-send-string process "CAP" "REQ" : cap)
(push cap rcirc-requested-capabilities))
(unless (zerop (length password))
(rcirc-send-string process "PASS" password))
(rcirc-send-string process "NICK" nick)
(rcirc-send-string process "USER" user-name "0" "*" : full-name)
;; Setup sasl, and initiate authentication.
(when (and rcirc-auto-authenticate-flag
use-sasl)
(rcirc-send-string process "AUTHENTICATE" "PLAIN"))
;; setup ping timer if necessary
(unless rcirc-keepalive-timer
(setq rcirc-keepalive-timer
(run-at-time 0 (/ rcirc-timeout-seconds 2) 'rcirc-keepalive)))
(message "Connecting to %s...done" (or server-alias server))
(setq mode-line-process nil)
;; return process object
process)))
;; return process object
process))))
(defmacro with-rcirc-process-buffer (process &rest body)
"Evaluate BODY in the buffer of PROCESS."
@ -795,31 +814,112 @@ When 0, do not auto-reconnect."
:version "25.1"
:type 'integer)
(defvar-local rcirc-last-connect-time nil
"The last time the buffer was connected.")
(defcustom rcirc-reconnect-attempts 3
"Number of times a reconnection should be attempted."
:version "28.1"
:type 'integer)
(defvar-local rcirc-failed-attempts 0
"Number of times reconnecting has failed.")
(defvar-local rcirc-reconnection-timer nil
"Timer used for reconnecting.")
(defun rcirc-reconnect (process &optional quiet)
"Attempt to reconnect connection to PROCESS.
If QUIET is non-nil, no not emit a message."
(with-rcirc-process-buffer process
(catch 'exit
(if (rcirc--connection-open-p process)
(throw 'exit (or quiet (message "Server process is alive")))
(delete-process process))
(let ((conn-info rcirc-connection-info))
(setf (nth 5 conn-info)
(cl-remove-if-not #'rcirc-channel-p
(mapcar #'car rcirc-buffer-alist)))
(dolist (buffer (mapcar #'cdr rcirc-buffer-alist))
(when (buffer-live-p buffer)
(with-current-buffer buffer
(setq mode-line-process ":connecting"))))
(let ((nprocess (apply #'rcirc-connect conn-info)))
(when (and (< rcirc-failed-attempts rcirc-reconnect-attempts)
(eq (process-status nprocess) 'failed))
(setq rcirc-failed-attempts (1+ rcirc-failed-attempts))
(rcirc-print nprocess "*rcirc*" "ERROR" nil
(format "Failed to reconnect (%d/%d)..."
rcirc-failed-attempts
rcirc-reconnect-attempts))
(setq rcirc-reconnection-timer
(run-at-time rcirc-timeout-seconds nil
#'rcirc-reconnect process t))))))))
(defun rcirc-sentinel (process sentinel)
"Called when PROCESS receives SENTINEL."
(let ((sentinel (string-replace "\n" "" sentinel)))
(rcirc-debug process (format "SENTINEL: %S %S\n" process sentinel))
(with-rcirc-process-buffer process
(dolist (buffer (cons nil (mapcar 'cdr rcirc-buffer-alist)))
(with-current-buffer (or buffer (current-buffer))
(rcirc-print process "rcirc.el" "ERROR" rcirc-target
(format "%s: %s (%S)"
(process-name process)
sentinel
(process-status process))
(not rcirc-target))
(rcirc-disconnect-buffer)))
(when (and (string= sentinel "deleted")
(< 0 rcirc-reconnect-delay))
(cond
((string= sentinel "open")
(let* ((server (nth 0 rcirc-connection-info))
(user-name (nth 3 rcirc-connection-info))
(full-name (nth 4 rcirc-connection-info))
(password (nth 6 rcirc-connection-info))
(server-alias (nth 8 rcirc-connection-info))
(use-sasl (eq (rcirc-get-server-method server) 'sasl)))
;; Prepare SASL authentication
(when use-sasl
(rcirc-send-string process "CAP REQ sasl")
(setq-local rcirc-finished-sasl nil))
;; Capability negotiation
(dolist (cap rcirc-implemented-capabilities)
(rcirc-send-string process "CAP" "REQ" : cap)
(push cap rcirc-requested-capabilities))
;; Identify user
(unless (zerop (length password))
(rcirc-send-string process "PASS" password))
(rcirc-send-string process "NICK" rcirc-nick)
(rcirc-send-string process "USER" user-name "0" "*" : full-name)
;; Setup sasl, and initiate authentication.
(when (and rcirc-auto-authenticate-flag
use-sasl)
(rcirc-send-string process "AUTHENTICATE" "PLAIN"))
;; Setup ping timer if necessary
(unless rcirc-keepalive-timer
(setq rcirc-keepalive-timer
(run-at-time 0 (/ rcirc-timeout-seconds 2) #'rcirc-keepalive)))
;; Reset previous reconnection attempts
(setq rcirc-failed-attempts 0)
(when rcirc-reconnection-timer
(cancel-timer rcirc-reconnection-timer)
(setq rcirc-reconnection-timer nil))
(message "Connecting to %s...done" (or server-alias server))
(dolist (buffer (cons nil (mapcar 'cdr rcirc-buffer-alist)))
(with-current-buffer (or buffer (current-buffer))
(setq mode-line-process nil)))))
((string= sentinel "deleted")
(let ((now (current-time)))
(when (or (null rcirc-last-connect-time)
(time-less-p rcirc-reconnect-delay
(time-subtract now rcirc-last-connect-time)))
(setq rcirc-last-connect-time now)
(rcirc-cmd-reconnect nil))))
(with-rcirc-process-buffer process
(when (and (< 0 rcirc-reconnect-delay)
(time-less-p rcirc-reconnect-delay
(time-subtract now rcirc-last-connect-time)))
(setq rcirc-last-connect-time now)
(rcirc-reconnect process)))))
((dolist (buffer (cons nil (mapcar 'cdr rcirc-buffer-alist)))
(with-current-buffer (or buffer (current-buffer))
(rcirc-print process "*rcirc*" "ERROR" rcirc-target
(format "%s: %s (%S)"
(process-name process)
sentinel
(process-status process))
(not rcirc-target))
(rcirc-disconnect-buffer)))))
(run-hook-with-args 'rcirc-sentinel-functions process sentinel))))
(defun rcirc-disconnect-buffer (&optional buffer)
@ -879,7 +979,7 @@ Function is called with PROCESS, COMMAND, SENDER, ARGS and LINE.")
(condition-case err
(rcirc-process-server-response-1 process text)
(error
(rcirc-print process "RCIRC" "ERROR" nil
(rcirc-print process "*rcirc*" "ERROR" nil
(format "\"%s\" %s" text err) t)))
(rcirc-process-server-response-1 process text)))
@ -1054,7 +1154,7 @@ With no argument or nil as argument, use the current buffer."
(let ((buffer (or buffer (and (buffer-live-p rcirc-server-buffer)
rcirc-server-buffer))))
(if buffer
(with-current-buffer buffer rcirc-process)
(buffer-local-value 'rcirc-process buffer)
rcirc-process)))
(defun rcirc-server-name (process)
@ -1258,7 +1358,8 @@ Each element looks like (FILENAME . TEXT).")
This number is independent of the number of lines in the buffer.")
(defun rcirc-mode (process target)
"Major mode for IRC channel buffers.
"Initialize an IRC buffer for writing with TARGET.
PROCESS is the process object used for communication.
\\{rcirc-mode-map}"
;; FIXME: Use define-derived-mode.
@ -1281,7 +1382,6 @@ This number is independent of the number of lines in the buffer.")
(setq rcirc-last-post-time (current-time))
(setq-local fill-paragraph-function 'rcirc-fill-paragraph)
(setq rcirc-current-line 0)
(setq rcirc-last-connect-time (current-time))
(use-hard-newlines t)
@ -1320,8 +1420,7 @@ This number is independent of the number of lines in the buffer.")
(when target ; skip server buffer
(let ((buffer (current-buffer)))
(with-rcirc-process-buffer process
(setq rcirc-buffer-alist (cons (cons target buffer)
rcirc-buffer-alist))))
(push (cons target buffer) rcirc-buffer-alist)))
(rcirc-update-short-buffer-names))
(add-hook 'completion-at-point-functions
@ -1464,10 +1563,11 @@ Create the buffer if it doesn't exist."
(rcirc-generate-new-buffer-name process target))))
(with-current-buffer new-buffer
(unless (eq major-mode 'rcirc-mode)
(rcirc-mode process target)))
(rcirc-mode process target))
(setq mode-line-process nil)
(rcirc-put-nick-channel process (rcirc-nick process) target
rcirc-current-line)
(setq rcirc-joined (current-time)))
(rcirc-put-nick-channel process (rcirc-nick process) target
rcirc-current-line)
new-buffer)))))
(defun rcirc-send-input ()
@ -1522,6 +1622,11 @@ The argument JUSTIFY is passed on to `fill-region'."
(defun rcirc-process-message (line)
"Process LINE as a message to be sent."
(when (and (null rcirc-target)
(string-match
(rx bos (group (+? nonl)) "@" (+ nonl) eos)
(buffer-name)))
(setq rcirc-target (match-string 1 (buffer-name))))
(if (not rcirc-target)
(message "Not joined (no target)")
(delete-region rcirc-prompt-end-marker (point))
@ -1625,6 +1730,9 @@ extracted."
("ACTION" . "[%N %m]")
("COMMAND" . "%m")
("ERROR" . "%fw!!! %m")
("FAIL" . "(%fwFAIL%f-) %m")
("WARN" . "(%fwWARN%f-) %m")
("NOTE" . "(%fwNOTE%f-) %m")
(t . "%fp*** %fs%n %r %m"))
"An alist of formats used for printing responses.
The format is looked up using the response-type as a key;
@ -1742,8 +1850,9 @@ Returns nil if the information is not recorded.
PROCESS is the process object for the current connection."
(let ((chanbuf (rcirc-get-buffer process target)))
(when chanbuf
(cdr (assoc-string nick (with-current-buffer chanbuf
rcirc-recent-quit-alist))))))
(cdr (assoc-string nick (buffer-local-value
'rcirc-recent-quit-alist
chanbuf))))))
(defun rcirc-last-line (process nick target)
"Return the line from the last activity from NICK in TARGET.
@ -1858,9 +1967,9 @@ connection."
(let ((last-activity-lines (rcirc-elapsed-lines process sender target)))
(if (and (not (string= (rcirc-nick process) sender))
(or (member response rcirc-omit-responses)
(if (member response rcirc-omit-after-reconnect)
rcirc-reconncting
(setq rcirc-reconncting nil)))
(and (member response rcirc-omit-responses-after-join)
(< (time-to-seconds (time-since rcirc-joined))
1)))
(or (not last-activity-lines)
(< rcirc-omit-threshold last-activity-lines)))
(put-text-property (point-min) (point-max)
@ -2008,7 +2117,8 @@ PROCESS is the process object for the current connection."
"Return the nick from USER. Remove any non-nick junk."
(save-match-data
(if (string-match (concat "^[" rcirc-nick-prefix-chars
"]?\\([^! ]+\\)!?") (or user ""))
"]*\\([^! ]+\\)!?")
(or user ""))
(match-string 1 user)
user)))
@ -2119,6 +2229,11 @@ This function does not alter the INPUT string."
map)
"Keymap for rcirc track minor mode.")
(defcustom rcirc-track-abbrevate-flag t
"Non-nil means `rcirc-track-minor-mode' should abbreviate names."
:version "28.1"
:type 'boolean)
;;;###autoload
(define-minor-mode rcirc-track-minor-mode
"Global minor mode for tracking activity in rcirc buffers."
@ -2176,7 +2291,7 @@ This function does not alter the INPUT string."
"Bury all RCIRC buffers."
(interactive)
(dolist (buf (buffer-list))
(when (eq 'rcirc-mode (with-current-buffer buf major-mode))
(when (eq 'rcirc-mode (buffer-local-value 'major-mode buf))
(bury-buffer buf) ; buffers not shown
(quit-windows-on buf)))) ; buffers shown in a window
@ -2216,13 +2331,15 @@ activity. Only run if the buffer is not visible and
(with-current-buffer buffer
(let ((old-activity rcirc-activity)
(old-types rcirc-activity-types))
(when (not (get-buffer-window (current-buffer) t))
(when (and (not (get-buffer-window (current-buffer) t))
(not (and rcirc-track-ignore-server-buffer-flag
(eq rcirc-server-buffer (current-buffer)))))
(setq rcirc-activity
(sort (if (memq (current-buffer) rcirc-activity) rcirc-activity
(cons (current-buffer) rcirc-activity))
(lambda (b1 b2)
(let ((t1 (with-current-buffer b1 rcirc-last-post-time))
(t2 (with-current-buffer b2 rcirc-last-post-time)))
(let ((t1 (buffer-local-value 'rcirc-last-post-time b1))
(t2 (buffer-local-value 'rcirc-last-post-time b2)))
(time-less-p t2 t1)))))
(cl-pushnew type rcirc-activity-types)
(unless (and (equal rcirc-activity old-activity)
@ -2299,7 +2416,12 @@ activity. Only run if the buffer is not visible and
(defun rcirc-short-buffer-name (buffer)
"Return a short name for BUFFER to use in the mode line indicator."
(with-current-buffer buffer
(or rcirc-short-buffer-name (buffer-name))))
(funcall rcirc-channel-filter
(replace-regexp-in-string
"@.*?\\'" ""
(or (and rcirc-track-abbrevate-flag
rcirc-short-buffer-name)
(buffer-name))))))
(defun rcirc-visible-buffers ()
"Return a list of the visible buffers that are in `rcirc-mode'."
@ -2408,7 +2530,7 @@ prefix with another element in PAIRS."
(when (and (listp x) (listp (cadr x)))
(setcdr x (if (> (length (cdr x)) 1)
(rcirc-make-trees (cdr x))
(setcdr x (list (cdadr x)))))))
(setcdr x (list (cdadr x)))))))
alist)))
;;; /commands these are called with 3 args: PROCESS, TARGET, which is
@ -2441,23 +2563,23 @@ that, an interactive form can specified."
(insert "\\(.*?\\)")
(insert "[[:space:]]*\\'")
(buffer-string)))
(argument (gensym))
(argument (make-symbol "arglist"))
documentation
interactive-spec)
(when (stringp (car body))
(setq documentation (pop body)))
(when (eq (car-safe (car-safe body)) 'interactive)
(setq interactive-spec (cdr (pop body))))
(setq interactive-spec (cadr (pop body))))
`(progn
(defun ,fn-name (,argument &optional process target)
,(concat documentation
"\n\nNote: If PROCESS or TARGET are nil, the values given"
"\nby `rcirc-buffer-process' and `rcirc-target' will be used.")
(interactive (list ,@interactive-spec))
(interactive ,interactive-spec)
(unless (if (listp ,argument)
(<= ,required (length ,argument) ,total)
(string-match ,regexp ,argument))
(user-error "Malformed input (%s): %S" ',command ',argument))
(user-error "Malformed input (%s): %S" ',command ,argument))
(let ((process (or process (rcirc-buffer-process)))
(target (or target rcirc-target)))
(ignore target process)
@ -2533,18 +2655,8 @@ to `rcirc-default-part-reason'."
(rcirc-define-command reconnect ()
"Reconnect to current server."
(interactive "i")
(with-rcirc-server-buffer
(cond
(rcirc-connecting (message "Already connecting"))
((process-live-p process) (message "Server process is alive"))
(t (let ((conn-info rcirc-connection-info))
(setf (nth 5 conn-info)
(cl-remove-if-not #'rcirc-channel-p
(mapcar #'car rcirc-buffer-alist)))
(dolist (buf (nth 5 conn-info))
(with-current-buffer (cdr (assoc buf rcirc-buffer-alist))
(setq rcirc-reconncting t)))
(apply #'rcirc-connect conn-info))))))
(setq rcirc-failed-attempts 0)
(rcirc-reconnect process))
(rcirc-define-command nick (nick)
"Change nick to NICK."
@ -2564,8 +2676,8 @@ With a prefix arg, prompt for new topic."
(interactive (list (and current-prefix-arg
(read-string "List names in channel: "))))
(if (> (length topic) 0)
(rcirc-send-string process "TOPIC" : topic)
(rcirc-send-string process "TOPIC")))
(rcirc-send-string process "TOPIC" target : topic)
(rcirc-send-string process "TOPIC" target)))
(rcirc-define-command whois (nick)
"Request information from server about NICK."
@ -3046,11 +3158,11 @@ connection."
;; already open buffer (after getting kicked e.g.)
(setq mode-line-process nil))
(rcirc-print process sender "JOIN" channel "")
(rcirc-print process sender "JOIN" (funcall rcirc-channel-filter channel) "")
;; print in private chat buffer if it exists
(when (rcirc-get-buffer (rcirc-buffer-process) sender)
(rcirc-print process sender "JOIN" sender channel))))
(rcirc-print process sender "JOIN" sender (funcall rcirc-channel-filter channel)))))
;; PART and KICK are handled the same way
(defun rcirc-handler-PART-or-KICK (process _response channel _sender nick _args)
@ -3079,10 +3191,10 @@ PROCESS is the process object for the current connection."
(let* ((channel (car args))
(reason (cadr args))
(message (concat channel " " reason)))
(rcirc-print process sender "PART" channel message)
(rcirc-print process sender "PART" (funcall rcirc-channel-filter channel) message)
;; print in private chat buffer if it exists
(when (rcirc-get-buffer (rcirc-buffer-process) sender)
(rcirc-print process sender "PART" sender message))
(rcirc-print process sender "PART" (funcall rcirc-channel-filter channel) message))
(rcirc-handler-PART-or-KICK process "PART" channel sender sender reason)))
@ -3094,7 +3206,7 @@ PROCESS is the process object for the current connection."
(nick (cadr args))
(reason (nth 2 args))
(message (concat nick " " channel " " reason)))
(rcirc-print process sender "KICK" channel message t)
(rcirc-print process sender "KICK" (funcall rcirc-channel-filter channel) message t)
;; print in private chat buffer if it exists
(when (rcirc-get-buffer (rcirc-buffer-process) nick)
(rcirc-print process sender "KICK" nick message))
@ -3124,7 +3236,7 @@ PROCESS is the process object for the current connection."
(rcirc-ignore-update-automatic sender)
(mapc (lambda (channel)
;; broadcast quit message each channel
(rcirc-print process sender "QUIT" channel (apply 'concat args))
(rcirc-print process sender "QUIT" (funcall rcirc-channel-filter channel) (apply 'concat args))
;; record nick in quit table if they recently spoke
(rcirc-maybe-remember-nick-quit process sender channel))
(rcirc-nick-channels process sender))
@ -3145,13 +3257,16 @@ PROCESS is the process object for the current connection."
;; print message to nick's channels
(dolist (target channels)
(rcirc-print process sender "NICK" target new-nick))
;; update private chat buffer, if it exists
(let ((chat-buffer (rcirc-get-buffer process old-nick)))
(when chat-buffer
(with-current-buffer chat-buffer
(rcirc-print process sender "NICK" old-nick new-nick)
(setq rcirc-target new-nick)
(rename-buffer (rcirc-generate-new-buffer-name process new-nick)))))
;; update chat buffer, if it exists
(when-let ((chat-buffer (rcirc-get-buffer process old-nick)))
(with-current-buffer chat-buffer
(rcirc-print process sender "NICK" old-nick new-nick)
(setq rcirc-target new-nick)
(rename-buffer (rcirc-generate-new-buffer-name process new-nick)))
(setf rcirc-buffer-alist
(cons (cons new-nick chat-buffer)
(delq (assoc-string old-nick rcirc-buffer-alist t)
rcirc-buffer-alist))))
;; remove old nick and add new one
(with-rcirc-process-buffer process
(let ((v (gethash old-nick rcirc-nick-table)))
@ -3234,7 +3349,7 @@ RFC1459."
(with-current-buffer buffer
(let ((setter (nth 2 args))
(time (current-time-string
(string-to-number (cadddr args)))))
(string-to-number (cadddr args)))))
(rcirc-print process sender "TOPIC" (cadr args)
(format "%s (%s on %s)" rcirc-topic setter time))))))
@ -3344,7 +3459,7 @@ Passwords are stored in `rcirc-authinfo' (which see)."
(server (car i))
(nick (nth 2 i))
(method (cadr i))
(args (cdddr i)))
(args (cdddr i)))
(when (and (string-match server rcirc-server))
(if (and (memq method '(nickserv chanserv bitlbee))
(string-match nick rcirc-nick))
@ -3381,6 +3496,8 @@ process object for the current connection."
(let ((self (buffer-local-value 'rcirc-nick rcirc-process))
(target (car args))
(chan (cadr args)))
;; `rcirc-channel-filter' is not used here because joining
;; requires an unfiltered name.
(if (string= target self)
(rcirc-print process sender "INVITE" nil
(format "%s invited you to %s"
@ -3451,7 +3568,7 @@ is the process object for the current connection."
(let ((subcmd (cadr args)))
(dolist (cap (cddr args))
(cond ((string= subcmd "ACK")
(push cap rcirc-acked-capabilities)
(push (intern (downcase cap)) rcirc-acked-capabilities)
(setq rcirc-requested-capabilities
(delete cap rcirc-requested-capabilities)))
((string= subcmd "NAK")
@ -3525,13 +3642,36 @@ PROCESS is the process object for the current connection."
"\0" (rcirc-get-server-password rcirc-server)))))
(defun rcirc-handler-900 (process sender args _text)
"Respond to a successful authentication response."
"Respond to a successful authentication response.
SENDER is passed on to `rcirc-handler-generic'. PROCESS is the
process object for the current connection."
(rcirc-handler-generic process "900" sender args nil)
(when (not rcirc-finished-sasl)
(setq-local rcirc-finished-sasl t)
(rcirc-send-string process "CAP" "END"))
(rcirc-join-channels-post-auth process))
(defun rcirc-handler-FAIL (process _sender args _text)
"Display a FAIL message, as indicated by ARGS.
PROCESS is the process object for the current connection."
(rcirc-print process nil "FAIL" nil
(mapconcat #'identity args " ")
t))
(defun rcirc-handler-WARN (process _sender args _text)
"Display a WARN message, as indicated by ARGS.
PROCESS is the process object for the current connection."
(rcirc-print process nil "WARN" nil
(mapconcat #'identity args " ")
t))
(defun rcirc-handler-NOTE (process _sender args _text)
"Display a NOTE message, as indicated by ARGS.
PROCESS is the process object for the current connection."
(rcirc-print process nil "NOTE" nil
(mapconcat #'identity args " ")
t))
(defgroup rcirc-faces nil
"Faces for rcirc."