[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 6ce957154b7 2/2: Redo erc-nickname-in-use-functions as a local mo
From: |
F. Jason Park |
Subject: |
master 6ce957154b7 2/2: Redo erc-nickname-in-use-functions as a local module |
Date: |
Sat, 1 Jul 2023 10:25:35 -0400 (EDT) |
branch: master
commit 6ce957154b701282217191d47764187535754529
Author: F. Jason Park <jp@neverwas.me>
Commit: F. Jason Park <jp@neverwas.me>
Redo erc-nickname-in-use-functions as a local module
* etc/ERC-NEWS: Mention new module `services-regain'.
* lisp/erc/erc-backend.el: Rename option.
* lisp/erc/erc-services.el (erc-services-regain-alist): Strategies for
regaining a lost nickname on reconnect. This option, in addition to
the rest of these changes, is a redo of
`erc-nickname-in-use-functions' from commit 8c0c9826 "Add hook to
regain nickname in ERC", which originally stemmed from bug#62044.
(erc-services-retry-nick-on-connect, erc-services-issue-regain,
erc-services-issue-ghost-and-retry-nick): New function variants for
`erc-services-regain-alist.
(erc-services-regain-mode, erc-services-regain-enable,
erc-services-regain-disable): New local module to activate
nick-regaining behavior.
(erc--nickname-in-use-make-request): New method, a services-specific
implementation.
* lisp/erc/erc.el (erc--nickname-in-use-make-request): New generic
function to request alternate nick when first choice is rejected.
(erc-nickname-in-use): Call `erc--nickname-in-use-make-request' to
request alternate nick.
* test/lisp/erc/erc-scenarios-services-misc.el
(erc-scenarios-services-misc--reconnect-retry-nick): Adopt renamed
version of `erc-scenarios-base-renick-auto-regain'.
(erc-scenarios-services-misc--regain-command,
erc-scenarios-services-misc--ghost-and-retry-nick): New tests.
* test/lisp/erc/resources/services/regain/reconnect-retry-again.eld:
New test data file reusing existing blob c0529052 that once lived at
resources/base/renick/regain/normal-again.eld.
* test/lisp/erc/resources/services/regain/reconnect-retry.eld: New
test data file reusing existing blob 9f4df70e5 that once lived at
resources/base/renick/regain/normal.eld.
* test/lisp/erc/resources/services/regain/taken-ghost.eld: New test
data file.
* test/lisp/erc/resources/services/regain/taken-regain.eld New test
data file.
---
etc/ERC-NEWS | 6 +-
lisp/erc/erc-backend.el | 4 +-
lisp/erc/erc-services.el | 121 +++++++++++++++++++++
lisp/erc/erc.el | 6 +-
test/lisp/erc/erc-scenarios-services-misc.el | 105 ++++++++++++++++++
.../services/regain/reconnect-retry-again.eld | 56 ++++++++++
.../resources/services/regain/reconnect-retry.eld | 53 +++++++++
.../erc/resources/services/regain/taken-ghost.eld | 42 +++++++
.../erc/resources/services/regain/taken-regain.eld | 42 +++++++
9 files changed, 430 insertions(+), 5 deletions(-)
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 68cf0e2d6ca..2f465e247d7 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -78,9 +78,9 @@ appearing in their saved logs.
** Smarter reconnect handling for users on the move.
ERC now offers a new, experimental reconnect strategy in the function
'erc-server-delayed-check-reconnect', which tests for underlying
-connectivity before attempting to reconnect in earnest. See options
-'erc-server-reconnect-function' and 'erc-nickname-in-use-functions' to
-get started.
+connectivity before attempting to reconnect in earnest. See option
+'erc-server-reconnect-function' and new local module 'services-regain'
+(also experimental) to get started.
** Module 'fill' can add a bit of space between messages.
On graphical displays, it's now possible to add some breathing room
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index b5bd96c189d..f1b51f9234a 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -427,7 +427,9 @@ This only has an effect if `erc-server-auto-reconnect' is
non-nil."
If this value is too low, servers may reject your initial nick
request upon reconnecting because they haven't yet noticed that
your previous connection is dead. If this happens, try setting
-this value to 120 or greater."
+this value to 120 or greater and/or exploring the option
+`erc-regain-services-alist', which may provide a more proactive
+means of handling this situation on some servers."
:type 'number)
(defcustom erc-server-reconnect-function 'erc-server-delayed-reconnect
diff --git a/lisp/erc/erc-services.el b/lisp/erc/erc-services.el
index 5408ba405db..47c59f76b5c 100644
--- a/lisp/erc/erc-services.el
+++ b/lisp/erc/erc-services.el
@@ -513,6 +513,127 @@ Returns t if the identify message could be sent, nil
otherwise."
nick)
nil))
+
+;;;; Regaining nicknames
+
+(defcustom erc-services-regain-alist nil
+ "Alist mapping networks to nickname-regaining functions.
+This option depends on the `services-regain' module being loaded.
+Keys can also be symbols for user-provided \"context IDs\" (see
+Info node `Network Identifier'). Functions run once, when first
+establishing a logical IRC connection. Although ERC currently
+calls them with one argument, the desired but rejected nickname,
+robust user implementations should leave room for later additions
+by defining an &rest _ parameter, as well.
+
+The simplest value is `erc-services-retry-nick-on-connect', which
+attempts to kill off stale connections without engaging services
+at all. Others, like `erc-services-issue-regain', and
+`erc-services-issue-ghost-and-retry-nick', only speak a
+particular flavor of NickServ. See their respective doc strings
+for details and use cases."
+ :package-version '(ERC . "5.6")
+ :group 'erc-hooks
+ :type '(alist :key-type (symbol :tag "Network")
+ :value-type
+ (choice :tag "Strategy function"
+ (function-item erc-services-retry-nick-on-connect)
+ (function-item erc-services-issue-regain)
+ (function-item erc-services-issue-ghost-and-retry-nick)
+ function)))
+
+(defun erc-services-retry-nick-on-connect (want)
+ "Try at most once to grab nickname WANT after reconnecting.
+Expect to be used when automatically reconnecting to servers
+that are slow to abandon the previous connection.
+
+Note that this strategy may only work under certain conditions,
+such as when a user's account name matches their nick."
+ (erc-cmd-NICK want))
+
+(defun erc-services-issue-regain (want)
+ "Ask NickServ to regain nickname WANT.
+Assume WANT belongs to the user and that the services suite
+offers a \"REGAIN\" sub-command."
+ (erc-cmd-MSG (concat "NickServ REGAIN " want)))
+
+(defun erc-services-issue-ghost-and-retry-nick (want)
+ "Ask NickServ to \"GHOST\" nickname WANT.
+After which, attempt to grab WANT before the contending party
+reconnects. Assume the ERC user owns WANT and that the server's
+services suite lacks a \"REGAIN\" command.
+
+Note that this function will only work for a specific services
+implementation and is meant primarily as an example for adapting
+as needed."
+ ;; While heuristics based on error text may seem brittle, consider
+ ;; the fact that \"is not online\" has been present in Atheme's
+ ;; \"GHOST\" responses since at least 2005.
+ (letrec ((attempts 3)
+ (on-notice
+ (lambda (_proc parsed)
+ (when-let ((nick (erc-extract-nick
+ (erc-response.sender parsed)))
+ ((erc-nick-equal-p nick "nickserv"))
+ (contents (erc-response.contents parsed))
+ (case-fold-search t)
+ ((string-match (rx (or "ghost" "is not online"))
+ contents)))
+ (setq attempts 1)
+ (erc-server-send (concat "NICK " want) 'force))
+ (when (zerop (cl-decf attempts))
+ (remove-hook 'erc-server-NOTICE-functions on-notice t))
+ nil)))
+ (add-hook 'erc-server-NOTICE-functions on-notice nil t)
+ (erc-message "PRIVMSG" (concat "NickServ GHOST " want))))
+
+;;;###autoload(put 'services-regain 'erc--feature 'erc-services)
+(define-erc-module services-regain nil
+ "Reacquire a nickname from your past self or some interloper.
+This module only concerns itself with initial nick rejections
+that occur during connection registration in response to an
+opening \"NICK\" command. More specifically, the following
+conditions must be met for ERC to activate this mechanism and
+consider its main option, `erc-services-regain-alist':
+
+ - the server must reject the opening \"NICK\" request
+ - ERC must request a temporary nickname
+ - the user must successfully authenticate
+
+In practical terms, this means that this module, which is still
+somewhat experimental, is likely only useful in conjunction with
+SASL authentication rather than the traditional approach provided
+by the `services' module it shares a library with (see Info
+node `(erc) SASL' for more)."
+ nil nil 'local)
+
+(cl-defmethod erc--nickname-in-use-make-request
+ ((want string) temp &context (erc-server-connected null)
+ (erc-services-regain-mode (eql t))
+ (erc-services-regain-alist cons))
+ "Schedule possible regain attempt upon establishing connection.
+Expect WANT to be the desired nickname and TEMP to be the current
+one."
+ (letrec
+ ((after-connect
+ (lambda (_ nick)
+ (remove-hook 'erc-after-connect after-connect t)
+ (when-let*
+ (((equal temp nick))
+ (conn (or (erc-networks--id-given erc-networks--id)
+ (erc-network)))
+ (found (alist-get conn erc-services-regain-alist)))
+ (funcall found want))))
+ (on-900
+ (lambda (_ parsed)
+ (remove-hook 'erc-server-900-functions on-900 t)
+ (unless erc-server-connected
+ (when (equal (car (erc-response.command-args parsed)) temp)
+ (add-hook 'erc-after-connect after-connect nil t)))
+ nil)))
+ (add-hook 'erc-server-900-functions on-900 nil t))
+ (cl-call-next-method))
+
(provide 'erc-services)
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 70adbb15b5f..e23185934f7 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -4930,6 +4930,10 @@ E.g. \"Read error to Nick [user@some.host]: 110\" would
be shortened to
(match-string 1 reason))
reason))
+(cl-defmethod erc--nickname-in-use-make-request (_nick temp)
+ "Request nickname TEMP in place of rejected NICK."
+ (erc-cmd-NICK temp))
+
(defun erc-nickname-in-use (nick reason)
"If NICK is unavailable, tell the user the REASON.
@@ -4963,7 +4967,7 @@ See also `erc-display-error-notice'."
;; established a connection yet
(- 9 (length erc-nick-uniquifier))))
erc-nick-uniquifier)))
- (erc-cmd-NICK newnick)
+ (erc--nickname-in-use-make-request nick newnick)
(erc-display-error-notice
nil
(format "Nickname %s is %s, trying %s"
diff --git a/test/lisp/erc/erc-scenarios-services-misc.el
b/test/lisp/erc/erc-scenarios-services-misc.el
index a1679d302f4..1113849578f 100644
--- a/test/lisp/erc/erc-scenarios-services-misc.el
+++ b/test/lisp/erc/erc-scenarios-services-misc.el
@@ -143,4 +143,109 @@
(erc-services-mode -1)))
+;; The server rejects your nick during registration, so ERC acquires a
+;; placeholder and successfully renicks once the connection is up.
+;; See also `erc-scenarios-base-renick-self-auto'.
+
+(ert-deftest erc-scenarios-services-misc--reconnect-retry-nick ()
+ :tags '(:expensive-test)
+ (erc-scenarios-common-with-cleanup
+ ((erc-server-flood-penalty 0.1)
+ (erc-scenarios-common-dialog "services/regain")
+ (dumb-server (erc-d-run "localhost" t 'reconnect-retry
+ 'reconnect-retry-again))
+ (port (process-contact dumb-server :service))
+ (erc-server-auto-reconnect t)
+ (erc-modules `(services-regain sasl ,@erc-modules))
+ (erc-services-regain-alist
+ '((Libera.Chat . erc-services-retry-nick-on-connect)))
+ (expect (erc-d-t-make-expecter)))
+
+ ;; FIXME figure out and explain why this is so.
+ (should (featurep 'erc-services))
+
+ (ert-info ("Session succeeds but cut short")
+ (with-current-buffer (erc :server "127.0.0.1"
+ :port port
+ :nick "tester"
+ :user "tester"
+ :password "changeme"
+ :full-name "tester")
+ (funcall expect 10 "Last login from")
+ (erc-cmd-JOIN "#test")))
+
+ (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#test"))
+ (funcall expect 10 "was created on"))
+
+ (ert-info ("Service restored")
+ (with-current-buffer "Libera.Chat"
+ (erc-d-t-wait-for 10 erc--server-reconnect-timer)
+ (funcall expect 10 "Connection failed!")
+ (funcall expect 10 "already in use")
+ (funcall expect 10 "changed mode for tester`")
+ (funcall expect 10 "Last login from")
+ (funcall expect 10 "Your new nickname is tester")))
+
+ (with-current-buffer (get-buffer "#test")
+ (funcall expect 10 "tester ")
+ (funcall expect 10 "was created on"))))
+
+;; This only asserts that the handler fires and issues the right
+;; NickServ command, but it doesn't accurately recreate a
+;; disconnection, but it probably should.
+(ert-deftest erc-scenarios-services-misc--regain-command ()
+ :tags '(:expensive-test)
+ (erc-scenarios-common-with-cleanup
+ ((erc-server-flood-penalty 0.1)
+ (erc-scenarios-common-dialog "services/regain")
+ (dumb-server (erc-d-run "localhost" t 'taken-regain))
+ (port (process-contact dumb-server :service))
+ (erc-server-auto-reconnect t)
+ (erc-modules `(services-regain sasl ,@erc-modules))
+ (erc-services-regain-alist
+ '((ExampleNet . erc-services-issue-regain)))
+ (expect (erc-d-t-make-expecter)))
+
+ (should (featurep 'erc-services)) ; see note in prior test
+
+ (with-current-buffer (erc :server "127.0.0.1"
+ :port port
+ :nick "dummy"
+ :user "tester"
+ :password "changeme"
+ :full-name "tester"
+ :id 'ExampleNet)
+ (funcall expect 10 "dummy is already in use, trying dummy`")
+ (funcall expect 10 "You are now logged in as tester")
+ (funcall expect 10 "-NickServ- dummy has been regained.")
+ (funcall expect 10 "*** Your new nickname is dummy")
+ ;; Works with "given" `:id'.
+ (should (and (erc-network) (not (eq (erc-network) 'ExampleNet)))))))
+
+(ert-deftest erc-scenarios-services-misc--ghost-and-retry-nick ()
+ :tags '(:expensive-test)
+ (erc-scenarios-common-with-cleanup
+ ((erc-server-flood-penalty 0.1)
+ (erc-scenarios-common-dialog "services/regain")
+ (dumb-server (erc-d-run "localhost" t 'taken-ghost))
+ (port (process-contact dumb-server :service))
+ (erc-server-auto-reconnect t)
+ (erc-modules `(services-regain sasl ,@erc-modules))
+ (erc-services-regain-alist
+ '((FooNet . erc-services-issue-ghost-and-retry-nick)))
+ (expect (erc-d-t-make-expecter)))
+
+ (should (featurep 'erc-services)) ; see note in prior test
+
+ (with-current-buffer (erc :server "127.0.0.1"
+ :port port
+ :nick "dummy"
+ :user "tester"
+ :password "changeme"
+ :full-name "tester")
+ (funcall expect 10 "dummy is already in use, trying dummy`")
+ (funcall expect 10 "You are now logged in as tester")
+ (funcall expect 10 "-NickServ- dummy has been ghosted.")
+ (funcall expect 10 "*** Your new nickname is dummy"))))
+
;;; erc-scenarios-services-misc.el ends here
diff --git a/test/lisp/erc/resources/services/regain/reconnect-retry-again.eld
b/test/lisp/erc/resources/services/regain/reconnect-retry-again.eld
new file mode 100644
index 00000000000..c0529052c70
--- /dev/null
+++ b/test/lisp/erc/resources/services/regain/reconnect-retry-again.eld
@@ -0,0 +1,56 @@
+;; -*- mode: lisp-data; -*-
+((cap 10 "CAP REQ :sasl"))
+((nick 10 "NICK tester"))
+((user 10 "USER tester 0 * :tester"))
+
+((authenticate 10 "AUTHENTICATE PLAIN")
+ (0.04 ":tantalum.libera.chat NOTICE * :*** Checking Ident")
+ (0.01 ":tantalum.libera.chat NOTICE * :*** Looking up your hostname...")
+ (0.01 ":tantalum.libera.chat NOTICE * :*** Couldn't look up your hostname")
+ (0.06 ":tantalum.libera.chat NOTICE * :*** No Ident response")
+ (0.02 ":tantalum.libera.chat CAP * ACK :sasl")
+ (0.03 ":tantalum.libera.chat 433 * tester :Nickname is already in use."))
+
+((nick 10 "NICK tester`")
+ (0.03 "AUTHENTICATE +"))
+
+((authenticate 10 "AUTHENTICATE AHRlc3RlcgBjaGFuZ2VtZQ==")
+ (0.06 ":tantalum.libera.chat 900 tester` tester`!tester@127.0.0.1 tester :You
are now logged in as tester")
+ (0.02 ":tantalum.libera.chat 903 tester` :SASL authentication successful"))
+
+((cap 10 "CAP END")
+ (0.02 ":tantalum.libera.chat 001 tester` :Welcome to the Libera.Chat Internet
Relay Chat Network tester`")
+ (0.02 ":tantalum.libera.chat 002 tester` :Your host is
tantalum.libera.chat[93.158.237.2/6697], running version solanum-1.0-dev")
+ (0.02 ":tantalum.libera.chat 003 tester` :This server was created Mon Feb 13
2023 at 12:05:04 UTC")
+ (0.01 ":tantalum.libera.chat 004 tester` tantalum.libera.chat solanum-1.0-dev
DGMQRSZaghilopsuwz CFILMPQRSTbcefgijklmnopqrstuvz bkloveqjfI")
+ (0.01 ":tantalum.libera.chat 005 tester` WHOX MONITOR=100 SAFELIST
ELIST=CMNTU ETRACE FNC CALLERID=g KNOCK CHANTYPES=# EXCEPTS INVEX
CHANMODES=eIbq,k,flj,CFLMPQRSTcgimnprstuz :are supported by this server")
+ (0.01 ":tantalum.libera.chat 005 tester` CHANLIMIT=#:250 PREFIX=(ov)@+
MAXLIST=bqeI:100 MODES=4 NETWORK=Libera.Chat STATUSMSG=@+ CASEMAPPING=rfc1459
NICKLEN=16 MAXNICKLEN=16 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by
this server")
+ (0.03 ":tantalum.libera.chat 005 tester`
TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR:
EXTBAN=$,ajrxz :are supported by this server")
+ (0.01 ":tantalum.libera.chat 251 tester` :There are 70 users and 42977
invisible on 28 servers")
+ (0.00 ":tantalum.libera.chat 252 tester` 38 :IRC Operators online")
+ (0.00 ":tantalum.libera.chat 253 tester` 87 :unknown connection(s)")
+ (0.00 ":tantalum.libera.chat 254 tester` 22908 :channels formed")
+ (0.00 ":tantalum.libera.chat 255 tester` :I have 2507 clients and 1 servers")
+ (0.00 ":tantalum.libera.chat 265 tester` 2507 3232 :Current local users 2507,
max 3232")
+ (0.00 ":tantalum.libera.chat 266 tester` 43047 51777 :Current global users
43047, max 51777")
+ (0.00 ":tantalum.libera.chat 250 tester` :Highest connection count: 3233
(3232 clients) (284887 connections received)")
+ (0.03 ":tantalum.libera.chat 375 tester` :- tantalum.libera.chat Message of
the Day - ")
+ (0.00 ":tantalum.libera.chat 372 tester` :- This server provided by
Hyperfilter (https://hyperfilter.com)")
+ (0.00 ":tantalum.libera.chat 372 tester` :- Email:
support@libera.chat")
+ (0.02 ":tantalum.libera.chat 376 tester` :End of /MOTD command."))
+
+((mode 10 "MODE tester` +i")
+ (0.01 ":tester` MODE tester` :+Ziw")
+ (0.02 ":SaslServ!SaslServ@services.libera.chat NOTICE tester` :Last login
from: \2~tester@127.0.0.1\2 on Apr 07 01:36:25 2023 +0000."))
+
+((nick 10 "NICK tester")
+ (0.02 ":tester`!~tester@127.0.0.1 NICK :tester"))
+
+((join 10 "JOIN #test")
+ (0.02 ":tester!~tester@127.0.0.1 JOIN #test")
+ (0.02 ":tantalum.libera.chat 353 tester = #test :tester zbyqbepbqre7
pusevgfpu Thrfg2187 zngbeb qnexNssvavgl wrebzr- rqpentt Ilehf grfg2 AvtugZbaxrl
pevfgvvbna xrivap_ fnvybePng shohxv gxan arrqyr avpx16 NeanhqW_kzcc jvyyr
wrnaogeq Wnarg cnefavc0 Xbentt RcvpArb flfqrs wfgbxre hafcrag__ Lbevpx_")
+ (0.02 ":tantalum.libera.chat 366 tester #test :End of /NAMES list."))
+
+((mode 10 "MODE #test")
+ (0.02 ":tantalum.libera.chat 324 tester #test +nt")
+ (0.02 ":tantalum.libera.chat 329 tester #test 1621432263"))
diff --git a/test/lisp/erc/resources/services/regain/reconnect-retry.eld
b/test/lisp/erc/resources/services/regain/reconnect-retry.eld
new file mode 100644
index 00000000000..9f4df70e580
--- /dev/null
+++ b/test/lisp/erc/resources/services/regain/reconnect-retry.eld
@@ -0,0 +1,53 @@
+;; -*- mode: lisp-data; -*-
+((cap 10 "CAP REQ :sasl"))
+((nick 10 "NICK tester"))
+((user 10 "USER tester 0 * :tester"))
+
+((authenticate 10 "AUTHENTICATE PLAIN")
+ (0.02 ":cadmium.libera.chat NOTICE * :*** Checking Ident")
+ (0.01 ":cadmium.libera.chat NOTICE * :*** Looking up your hostname...")
+ (0.01 ":cadmium.libera.chat NOTICE * :*** Couldn't look up your hostname")
+ (0.06 ":cadmium.libera.chat NOTICE * :*** No Ident response")
+ (0.09 ":cadmium.libera.chat CAP * ACK :sasl")
+ (0.01 "AUTHENTICATE +"))
+
+((authenticate 10 "AUTHENTICATE AHRlc3RlcgBjaGFuZ2VtZQ==")
+ (0.03 ":cadmium.libera.chat 900 tester tester!tester@127.0.0.1 tester :You
are now logged in as tester")
+ (0.01 ":cadmium.libera.chat 903 tester :SASL authentication successful"))
+
+((cap 10 "CAP END")
+ (0.03 ":cadmium.libera.chat 001 tester :Welcome to the Libera.Chat Internet
Relay Chat Network tester")
+ (0.02 ":cadmium.libera.chat 002 tester :Your host is
cadmium.libera.chat[103.196.37.95/6697], running version solanum-1.0-dev")
+ (0.01 ":cadmium.libera.chat 003 tester :This server was created Wed Jan 25
2023 at 10:22:45 UTC")
+ (0.01 ":cadmium.libera.chat 004 tester cadmium.libera.chat solanum-1.0-dev
DGMQRSZaghilopsuwz CFILMPQRSTbcefgijklmnopqrstuvz bkloveqjfI")
+ (0.00 ":cadmium.libera.chat 005 tester CALLERID=g WHOX ETRACE FNC SAFELIST
ELIST=CMNTU KNOCK MONITOR=100 CHANTYPES=# EXCEPTS INVEX
CHANMODES=eIbq,k,flj,CFLMPQRSTcgimnprstuz :are supported by this server")
+ (0.01 ":cadmium.libera.chat 005 tester CHANLIMIT=#:250 PREFIX=(ov)@+
MAXLIST=bqeI:100 MODES=4 NETWORK=Libera.Chat STATUSMSG=@+ CASEMAPPING=rfc1459
NICKLEN=16 MAXNICKLEN=16 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by
this server")
+ (0.01 ":cadmium.libera.chat 005 tester
TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR:
EXTBAN=$,ajrxz :are supported by this server")
+ (0.01 ":cadmium.libera.chat 251 tester :There are 70 users and 42996
invisible on 28 servers")
+ (0.02 ":cadmium.libera.chat 252 tester 38 :IRC Operators online")
+ (0.01 ":cadmium.libera.chat 253 tester 57 :unknown connection(s)")
+ (0.01 ":cadmium.libera.chat 254 tester 22912 :channels formed")
+ (0.01 ":cadmium.libera.chat 255 tester :I have 2499 clients and 1 servers")
+ (0.01 ":cadmium.libera.chat 265 tester 2499 4187 :Current local users 2499,
max 4187")
+ (0.01 ":cadmium.libera.chat 266 tester 43066 51827 :Current global users
43066, max 51827")
+ (0.01 ":cadmium.libera.chat 250 tester :Highest connection count: 4188 (4187
clients) (319420 connections received)")
+ (0.01 ":cadmium.libera.chat 375 tester :- cadmium.libera.chat Message of the
Day - ")
+ (0.01 ":cadmium.libera.chat 372 tester :- This server kindly provided by Mach
Dilemma (www.m-d.net)")
+ (0.01 ":cadmium.libera.chat 372 tester :- Welcome to Libera Chat, the IRC
network for")
+ (0.00 ":cadmium.libera.chat 372 tester :- Email:
support@libera.chat")
+ (0.00 ":cadmium.libera.chat 376 tester :End of /MOTD command.")
+ (0.00 ":tester MODE tester :+Ziw")
+ (0.02 ":SaslServ!SaslServ@services.libera.chat NOTICE tester :Last login
from: \2~tester@127.0.0.1\2 on Apr 07 01:02:11 2023 +0000."))
+
+((mode 10 "MODE tester +i"))
+
+((join 10 "JOIN #test")
+ (0.09 ":tester!~tester@127.0.0.1 JOIN #test"))
+
+((mode 10 "MODE #test")
+ (0.03 ":cadmium.libera.chat 353 tester = #test :tester zbyqbepbqre7 pusevgfpu
Thrfg2187 zngbeb qnexNssvavgl wrebzr- rqpentt Ilehf grfg2 AvtugZbaxrl
pevfgvvbna xrivap_ fnvybePng shohxv gxan arrqyr avpx16 NeanhqW_kzcc Lbevpx_
hafcrag__ wfgbxre flfqrs RcvpArb Xbentt jvyyr cnefavc0 Wnarg wrnaogeq")
+ (0.02 ":cadmium.libera.chat 366 tester #test :End of /NAMES list.")
+ (0.00 ":cadmium.libera.chat 324 tester #test +nt")
+ (0.01 ":cadmium.libera.chat 329 tester #test 1621432263"))
+
+((drop 0 DROP))
diff --git a/test/lisp/erc/resources/services/regain/taken-ghost.eld
b/test/lisp/erc/resources/services/regain/taken-ghost.eld
new file mode 100644
index 00000000000..d5afd124a43
--- /dev/null
+++ b/test/lisp/erc/resources/services/regain/taken-ghost.eld
@@ -0,0 +1,42 @@
+;; -*- mode: lisp-data; -*-
+((cap 10 "CAP REQ :sasl")
+ (0.00 ":irc.example.net NOTICE * :*** Looking up your hostname...")
+ (0.01 ":irc.example.net NOTICE * :*** Could not resolve your hostname: Domain
not found; using your IP address (10.0.2.100) instead."))
+((nick 10 "NICK dummy"))
+((user 10 "USER dummy 0 * :tester"))
+((authenticate 10 "AUTHENTICATE PLAIN")
+ (0.00 ":irc.example.net CAP * ACK :sasl")
+ (0.03 ":irc.example.net 433 * dummy :Nickname is already in use.")
+ (0.04 "AUTHENTICATE :+"))
+((nick 10 "NICK dummy`")
+ (0.00 "PING :orrMOjk^|V"))
+((~pong 10 "PONG :orrMOjk^|V"))
+((authenticate 10 "AUTHENTICATE AHRlc3RlcgBjaGFuZ2VtZQ==")
+ (0.01 ":irc.example.net 900 dummy` dummy`!dummy@10.0.2.100 tester :You are
now logged in as tester")
+ (0.01 ":irc.example.net 903 dummy` :SASL authentication successful"))
+((cap 10 "CAP END")
+ (0.00 ":irc.example.net 001 dummy` :Welcome to the FooNet IRC Network
dummy`!dummy@10.0.2.100")
+ (0.03 ":irc.example.net 002 dummy` :Your host is irc.example.net, running
version InspIRCd-3")
+ (0.01 ":irc.example.net 003 dummy` :This server was created 13:01:55 Jun 08
2023")
+ (0.01 ":irc.example.net 004 dummy` irc.example.net InspIRCd-3 BIRcgikorsw
ACHIKMORTXabcefghijklmnopqrstvz :HIXabefghjkloqv")
+ (0.00 ":irc.example.net 005 dummy` ACCEPT=30 AWAYLEN=200 BOT=B CALLERID=g
CASEMAPPING=ascii CHANLIMIT=#:20 CHANMODES=IXbeg,k,Hfjl,ACKMORTcimnprstz
CHANNELLEN=64 CHANTYPES=# ELIST=CMNTU ESILENCE=CcdiNnPpTtx EXCEPTS=e :are
supported by this server")
+ (0.01 ":irc.example.net 005 dummy` EXTBAN=,ACORTUacjrwz HOSTLEN=64 INVEX=I
KEYLEN=32 KICKLEN=255 LINELEN=512 MAXLIST=I:100,X:100,b:100,e:100,g:100
MAXTARGETS=20 MODES=20 MONITOR=30 NAMELEN=128 NAMESX NETWORK=FooNet :are
supported by this server")
+ (0.01 ":irc.example.net 005 dummy` NICKLEN=30 PREFIX=(qaohv)~&@%+ SAFELIST
SILENCE=32 STATUSMSG=~&@%+ TOPICLEN=307 UHNAMES USERIP USERLEN=10
USERMODES=,,s,BIRcgikorw WHOX :are supported by this server")
+ (0.01 ":irc.example.net 251 dummy` :There are 2 users and 1 invisible on 2
servers")
+ (0.01 ":irc.example.net 253 dummy` 1 :unknown connections")
+ (0.00 ":irc.example.net 254 dummy` 1 :channels formed")
+ (0.00 ":irc.example.net 255 dummy` :I have 3 clients and 1 servers")
+ (0.00 ":irc.example.net 265 dummy` :Current local users: 3 Max: 4")
+ (0.00 ":irc.example.net 266 dummy` :Current global users: 3 Max: 4")
+ (0.00 ":irc.example.net 375 dummy` :irc.example.net message of the day")
+ (0.00 ":irc.example.net 372 dummy` : Have fun with the image!")
+ (0.00 ":irc.example.net 376 dummy` :End of message of the day."))
+
+((mode 10 "MODE dummy` +i"))
+((privmsg 10 "PRIVMSG NickServ :GHOST dummy")
+ (0.00 ":irc.example.net 501 dummy` x :is not a recognised user mode.")
+ (0.00 ":irc.example.net NOTICE dummy` :*** You are connected to
irc.example.net using TLS (SSL) cipher 'TLS1.3-ECDHE-RSA-AES-256-GCM-AEAD'")
+ (0.03 ":dummy`!dummy@10.0.2.100 MODE dummy` :+i")
+ (0.02 ":NickServ!NickServ@services.int NOTICE dummy` :\2dummy\2 has been
ghosted."))
+((nick 10 "NICK dummy")
+ (0.02 ":dummy`!dummy@10.0.2.100 NICK :dummy"))
diff --git a/test/lisp/erc/resources/services/regain/taken-regain.eld
b/test/lisp/erc/resources/services/regain/taken-regain.eld
new file mode 100644
index 00000000000..22635d4cc89
--- /dev/null
+++ b/test/lisp/erc/resources/services/regain/taken-regain.eld
@@ -0,0 +1,42 @@
+;; -*- mode: lisp-data; -*-
+((cap 10 "CAP REQ :sasl")
+ (0.00 ":irc.example.net NOTICE * :*** Looking up your hostname...")
+ (0.01 ":irc.example.net NOTICE * :*** Could not resolve your hostname: Domain
not found; using your IP address (10.0.2.100) instead."))
+((nick 10 "NICK dummy"))
+((user 10 "USER dummy 0 * :tester"))
+;; This also happens to a test late ACK (see ghost variant for server-sent
PING)
+((authenticate 10 "AUTHENTICATE PLAIN")
+ (0.00 ":irc.example.net CAP * ACK :sasl")
+ (0.09 ":irc.example.net 433 * dummy :Nickname is already in use.")
+ (0.04 "AUTHENTICATE :+"))
+((nick 10 "NICK dummy`"))
+((authenticate 10 "AUTHENTICATE AHRlc3RlcgBjaGFuZ2VtZQ==")
+ (0.00 ":irc.example.net 900 dummy` dummy`!dummy@10.0.2.100 tester :You are
now logged in as tester")
+ (0.01 ":irc.example.net 903 dummy` :SASL authentication successful"))
+
+((cap 10 "CAP END")
+ (0.00 ":irc.example.net 001 dummy` :Welcome to the FooNet IRC Network
dummy`!dummy@10.0.2.100")
+ (0.02 ":irc.example.net 002 dummy` :Your host is irc.example.net, running
version InspIRCd-3")
+ (0.02 ":irc.example.net 003 dummy` :This server was created 08:16:52 Jun 08
2023")
+ (0.01 ":irc.example.net 004 dummy` irc.example.net InspIRCd-3 BIRcgikorsw
ACHIKMORTXabcefghijklmnopqrstvz :HIXabefghjkloqv")
+ (0.00 ":irc.example.net 005 dummy` ACCEPT=30 AWAYLEN=200 BOT=B CALLERID=g
CASEMAPPING=ascii CHANLIMIT=#:20 CHANMODES=IXbeg,k,Hfjl,ACKMORTcimnprstz
CHANNELLEN=64 CHANTYPES=# ELIST=CMNTU ESILENCE=CcdiNnPpTtx EXCEPTS=e :are
supported by this server")
+ (0.01 ":irc.example.net 005 dummy` EXTBAN=,ACORTUacjrwz HOSTLEN=64 INVEX=I
KEYLEN=32 KICKLEN=255 LINELEN=512 MAXLIST=I:100,X:100,b:100,e:100,g:100
MAXTARGETS=20 MODES=20 MONITOR=30 NAMELEN=128 NAMESX NETWORK=FooNet :are
supported by this server")
+ (0.01 ":irc.example.net 005 dummy` NICKLEN=30 PREFIX=(qaohv)~&@%+ SAFELIST
SILENCE=32 STATUSMSG=~&@%+ TOPICLEN=307 UHNAMES USERIP USERLEN=10
USERMODES=,,s,BIRcgikorw WHOX :are supported by this server")
+ (0.01 ":irc.example.net 251 dummy` :There are 2 users and 1 invisible on 2
servers")
+ (0.01 ":irc.example.net 253 dummy` 1 :unknown connections")
+ (0.00 ":irc.example.net 254 dummy` 1 :channels formed")
+ (0.02 ":irc.example.net 255 dummy` :I have 3 clients and 1 servers")
+ (0.00 ":irc.example.net 265 dummy` :Current local users: 3 Max: 4")
+ (0.00 ":irc.example.net 266 dummy` :Current global users: 3 Max: 4")
+ (0.00 ":irc.example.net 375 dummy` :irc.example.net message of the day")
+ (0.00 ":irc.example.net 372 dummy` : Have fun with the image!")
+ (0.00 ":irc.example.net 376 dummy` :End of message of the day.")
+ (0.00 ":irc.example.net 501 dummy` x :is not a recognised user mode.")
+ (0.00 ":irc.example.net NOTICE dummy` :*** You are connected to
irc.example.net using TLS (SSL) cipher 'TLS1.3-ECDHE-RSA-AES-256-GCM-AEAD'"))
+
+((mode 10 "MODE dummy` +i"))
+
+((privmsg 10 "PRIVMSG NickServ :REGAIN dummy")
+ (0.00 ":dummy`!dummy@10.0.2.100 MODE dummy` :+i")
+ (0.02 ":NickServ!NickServ@services.int NOTICE dummy` :\2dummy\2 has been
regained.")
+ (0.02 ":dummy`!dummy@10.0.2.100 NICK :dummy"))