]> git.eshelyaron.com Git - emacs.git/commitdiff
Redo erc-nickname-in-use-functions as a local module
authorF. Jason Park <jp@neverwas.me>
Fri, 9 Jun 2023 06:49:23 +0000 (23:49 -0700)
committerF. Jason Park <jp@neverwas.me>
Sat, 1 Jul 2023 14:21:30 +0000 (07:21 -0700)
* 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
lisp/erc/erc-backend.el
lisp/erc/erc-services.el
lisp/erc/erc.el
test/lisp/erc/erc-scenarios-services-misc.el
test/lisp/erc/resources/services/regain/reconnect-retry-again.eld [new file with mode: 0644]
test/lisp/erc/resources/services/regain/reconnect-retry.eld [new file with mode: 0644]
test/lisp/erc/resources/services/regain/taken-ghost.eld [new file with mode: 0644]
test/lisp/erc/resources/services/regain/taken-regain.eld [new file with mode: 0644]

index 68cf0e2d6ca71c98a5f8ed465efb26452dbc7bf9..2f465e247d7fe3d99f0618e6b1b8fc1392417359 100644 (file)
@@ -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
index b5bd96c189d8aa64681448fa199229a50f370f2b..f1b51f9234a377a80b3aa3aa34c22dc0c05791d3 100644 (file)
@@ -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
index 5408ba405db7c441016d6d922fa74d85743459f8..47c59f76b5ce946e3a5c5c35e5a64ef48f06edf4 100644 (file)
@@ -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)
 
 
index 70adbb15b5f56106a146e3d5ee36e88d66d5871b..e23185934f7bcfb91a1a5279e0aba8cdef60ec65 100644 (file)
@@ -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"
index a1679d302f4d4429a4d1303ecbf5155a049cd936..1113849578f4588cda0a3b0ac16c3d9af681f5a2 100644 (file)
 
     (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 (file)
index 0000000..c052905
--- /dev/null
@@ -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 (file)
index 0000000..9f4df70
--- /dev/null
@@ -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 (file)
index 0000000..d5afd12
--- /dev/null
@@ -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 (file)
index 0000000..22635d4
--- /dev/null
@@ -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"))