diff options
| author | F. Jason Park | 2022-12-25 21:36:53 -0800 |
|---|---|---|
| committer | F. Jason Park | 2022-12-28 06:51:38 -0800 |
| commit | 2ddc480f4417775d6bf8ebcfc27b8cd7fa761a7d (patch) | |
| tree | c37c55716af7350cd74f3cac577dc9ccd96be8d4 | |
| parent | 19d00fab9aaf28dae6af5786f6e22b8558b10eea (diff) | |
| download | emacs-2ddc480f4417775d6bf8ebcfc27b8cd7fa761a7d.tar.gz emacs-2ddc480f4417775d6bf8ebcfc27b8cd7fa761a7d.zip | |
Warn of absent networks module in ERC
* doc/misc/erc.texi: Add linkable note in Modules chapter about some
modules being required. Also tweak markup in auth-source section.
* etc/ERC-NEWS: Mention the special role of `networks'.
* lisp/erc/erc-backend.el (erc--server-post-connect-hook): Add
internal hook for core modules to perform post-network-process,
pre-protocol config validation even when they haven't been loaded.
(erc--register-connection): Run `erc--server-post-connect-hook'.
* lisp/erc/erc-networks.el (erc-networks--bouncer-targets,
erc-networks-on-MOTD-end): Fix comments and doc strings. Also change
former from constant to internal variable in case adjustment needed
between releases.
(erc-networks--warn-on-connect): New function to warn about the
`networks' module being absent from `erc-modules'. This could
probably run at any time up to and including when the logical IRC
connection is established, but doing so at the process/protocol
boundary seems ideal.
* lisp/erc/erc-sasl.el (erc--register-connection): Defer to base
method instead of calling `erc-login' explicitly.
* lisp/erc/erc.el (erc-generate-new-buffer-name): Don't reconcile
buffer names when networks module not in play.
(erc-format-target-and/or-network): Don't assume networks module
loaded.
* test/lisp/erc/erc-scenarios-base-unstable.el:
(erc-scenarios-networks-no-module): New test.
* test/lisp/erc/resources/networks/no-module/basic.eld: New test data
file. (Bug#60331.)
| -rw-r--r-- | doc/misc/erc.texi | 12 | ||||
| -rw-r--r-- | etc/ERC-NEWS | 11 | ||||
| -rw-r--r-- | lisp/erc/erc-backend.el | 10 | ||||
| -rw-r--r-- | lisp/erc/erc-networks.el | 26 | ||||
| -rw-r--r-- | lisp/erc/erc-sasl.el | 2 | ||||
| -rw-r--r-- | lisp/erc/erc.el | 6 | ||||
| -rw-r--r-- | test/lisp/erc/erc-scenarios-base-unstable.el | 54 | ||||
| -rw-r--r-- | test/lisp/erc/resources/networks/no-module/basic.eld | 44 |
8 files changed, 153 insertions, 12 deletions
diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi index 2ab2e908940..249b58c73d8 100644 --- a/doc/misc/erc.texi +++ b/doc/misc/erc.texi | |||
| @@ -529,6 +529,16 @@ Translate morse code in messages | |||
| 529 | 529 | ||
| 530 | @end table | 530 | @end table |
| 531 | 531 | ||
| 532 | @anchor{Required Modules} | ||
| 533 | @subheading Required Modules | ||
| 534 | @cindex required modules | ||
| 535 | |||
| 536 | Note that some modules are essential to core IRC operations and thus | ||
| 537 | not listed above. You can nevertheless still remove these, but doing | ||
| 538 | so demands special precautions to avoid degrading the user experience. | ||
| 539 | At present, the only such module is @code{networks}, whose library ERC | ||
| 540 | always loads anyway. | ||
| 541 | |||
| 532 | @subheading Local Modules | 542 | @subheading Local Modules |
| 533 | @cindex local modules | 543 | @cindex local modules |
| 534 | 544 | ||
| @@ -1290,7 +1300,7 @@ When preparing entries for your backend, it may help to get a feel for | |||
| 1290 | how ERC and its modules conduct searches, especially when exploring a | 1300 | how ERC and its modules conduct searches, especially when exploring a |
| 1291 | new context, such as channel keys. (Hint: in such situations, try | 1301 | new context, such as channel keys. (Hint: in such situations, try |
| 1292 | temporarily setting the variable @code{auth-source-debug} to @code{t} | 1302 | temporarily setting the variable @code{auth-source-debug} to @code{t} |
| 1293 | and checking @samp{*Messages*} periodically for insights into how | 1303 | and checking @file{*Messages*} periodically for insights into how |
| 1294 | auth-source is operating.) Overall, though, ERC tries to be | 1304 | auth-source is operating.) Overall, though, ERC tries to be |
| 1295 | consistent in performing queries across various authentication | 1305 | consistent in performing queries across various authentication |
| 1296 | contexts. Here's what to expect with respect to the @samp{host} | 1306 | contexts. Here's what to expect with respect to the @samp{host} |
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS index 76439f1d068..b577047ebcb 100644 --- a/etc/ERC-NEWS +++ b/etc/ERC-NEWS | |||
| @@ -39,6 +39,14 @@ anew. The pre-5.4 "disabled" behavior has been restored and will | |||
| 39 | remain accessible for the foreseeable future, warts and all (e.g., | 39 | remain accessible for the foreseeable future, warts and all (e.g., |
| 40 | with its often superfluous "/DIALED-HOST" suffixing always present). | 40 | with its often superfluous "/DIALED-HOST" suffixing always present). |
| 41 | 41 | ||
| 42 | ** The 'networks' module is now quasi-required. | ||
| 43 | The 'networks' module is now all but required for everyday interactive | ||
| 44 | use. A default member of 'erc-modules' since ERC 5.3, 'networks' has | ||
| 45 | grown increasingly integral to core client operations over the years. | ||
| 46 | From now on, only the most essential operations will be officially | ||
| 47 | supported in its absence, and users will see a warning upon | ||
| 48 | entry-point invocation when it's not present. | ||
| 49 | |||
| 42 | ** Tighter auth-source integration with bigger changes on the horizon. | 50 | ** Tighter auth-source integration with bigger changes on the horizon. |
| 43 | The days of hit-and-miss auth-source queries are hopefully behind us. | 51 | The days of hit-and-miss auth-source queries are hopefully behind us. |
| 44 | With the overhaul of the services module temporarily shelved and the | 52 | With the overhaul of the services module temporarily shelved and the |
| @@ -111,7 +119,8 @@ and 'erc-backend'. | |||
| 111 | 119 | ||
| 112 | The function 'erc-network' always returns non-nil in server and target | 120 | The function 'erc-network' always returns non-nil in server and target |
| 113 | buffers belonging to a successfully established IRC connection, even | 121 | buffers belonging to a successfully established IRC connection, even |
| 114 | after that connection has been closed. | 122 | after that connection has been closed. (Also see the note in the |
| 123 | section above about the 'networks' module basically being mandatory.) | ||
| 115 | 124 | ||
| 116 | In 5.4, support for network symbols as keys was added for | 125 | In 5.4, support for network symbols as keys was added for |
| 117 | 'erc-autojoin-channels-alist'. This has been extended to include | 126 | 'erc-autojoin-channels-alist'. This has been extended to include |
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index 43c5faad638..6820bf0d1a3 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el | |||
| @@ -320,6 +320,15 @@ session when reconnecting. Once `erc-reuse-buffers' is retired | |||
| 320 | and fully removed, modules can switch to leveraging the | 320 | and fully removed, modules can switch to leveraging the |
| 321 | `permanent-local' property instead.") | 321 | `permanent-local' property instead.") |
| 322 | 322 | ||
| 323 | (defvar erc--server-post-connect-hook '(erc-networks--warn-on-connect) | ||
| 324 | "Functions to run when a network connection is successfully opened. | ||
| 325 | Though internal, this complements `erc-connect-pre-hook' in that | ||
| 326 | it bookends the process rather than the logical connection, which | ||
| 327 | is the domain of `erc-before-connect' and `erc-after-connect'. | ||
| 328 | Note that unlike `erc-connect-pre-hook', this only runs in server | ||
| 329 | buffers, and it does so immediately before the first protocol | ||
| 330 | exchange.") | ||
| 331 | |||
| 323 | (defvar-local erc-server-timed-out nil | 332 | (defvar-local erc-server-timed-out nil |
| 324 | "Non-nil if the IRC server failed to respond to a ping.") | 333 | "Non-nil if the IRC server failed to respond to a ping.") |
| 325 | 334 | ||
| @@ -646,6 +655,7 @@ The current buffer is given by BUFFER." | |||
| 646 | 655 | ||
| 647 | (cl-defmethod erc--register-connection () | 656 | (cl-defmethod erc--register-connection () |
| 648 | "Perform opening IRC protocol exchange with server." | 657 | "Perform opening IRC protocol exchange with server." |
| 658 | (run-hooks 'erc--server-post-connect-hook) | ||
| 649 | (erc-login)) | 659 | (erc-login)) |
| 650 | 660 | ||
| 651 | (defvar erc--server-connect-dumb-ipv6-regexp | 661 | (defvar erc--server-connect-dumb-ipv6-regexp |
diff --git a/lisp/erc/erc-networks.el b/lisp/erc/erc-networks.el index 2e2d0930118..f05a98be16d 100644 --- a/lisp/erc/erc-networks.el +++ b/lisp/erc/erc-networks.el | |||
| @@ -1472,14 +1472,16 @@ to be a false alarm. If `erc-reuse-buffers' is nil, let | |||
| 1472 | (t (rename-buffer (generate-new-buffer-name name))))) | 1472 | (t (rename-buffer (generate-new-buffer-name name))))) |
| 1473 | nil) | 1473 | nil) |
| 1474 | 1474 | ||
| 1475 | ;; Soju v0.4.0 only sends ISUPPORT on upstream reconnect, so this | 1475 | ;; Soju v0.4.0 sends ISUPPORT and nothing else on upstream reconnect, |
| 1476 | ;; doesn't apply. ZNC 1.8.2, however, still sends the entire burst. | 1476 | ;; so this actually doesn't apply. ZNC 1.8.2, however, still sends |
| 1477 | (defconst erc-networks--bouncer-targets '(*status bouncerserv) | 1477 | ;; the entire burst. |
| 1478 | "Case-mapped symbols matching known bouncer service-bot targets.") | 1478 | (defvar erc-networks--bouncer-targets '(*status bouncerserv) |
| 1479 | "Symbols matching proxy-bot targets.") | ||
| 1479 | 1480 | ||
| 1480 | (defun erc-networks-on-MOTD-end (proc parsed) | 1481 | (defun erc-networks-on-MOTD-end (proc parsed) |
| 1481 | "Call on-connect functions with server PROC and PARSED message. | 1482 | "Call on-connect functions with server PROC and PARSED message." |
| 1482 | This must run before `erc-server-connected' is set." | 1483 | ;; This should normally run before `erc-server-connected' is set. |
| 1484 | ;; However, bouncers and other proxies may interfere with that. | ||
| 1483 | (when erc-server-connected | 1485 | (when erc-server-connected |
| 1484 | (unless (erc-buffer-filter (lambda () | 1486 | (unless (erc-buffer-filter (lambda () |
| 1485 | (and erc--target | 1487 | (and erc--target |
| @@ -1502,6 +1504,18 @@ This must run before `erc-server-connected' is set." | |||
| 1502 | ((remove-hook 'erc-server-376-functions #'erc-networks-on-MOTD-end) | 1504 | ((remove-hook 'erc-server-376-functions #'erc-networks-on-MOTD-end) |
| 1503 | (remove-hook 'erc-server-422-functions #'erc-networks-on-MOTD-end))) | 1505 | (remove-hook 'erc-server-422-functions #'erc-networks-on-MOTD-end))) |
| 1504 | 1506 | ||
| 1507 | (defun erc-networks--warn-on-connect () | ||
| 1508 | "Emit warning when the `networks' module hasn't been loaded. | ||
| 1509 | Ideally, do so upon opening the network process." | ||
| 1510 | (unless (or erc--target erc-networks-mode) | ||
| 1511 | (require 'info nil t) | ||
| 1512 | (let ((m (concat "Required module `networks' not loaded. If this " | ||
| 1513 | " was unexpected, please add it to `erc-modules'."))) | ||
| 1514 | ;; Assume the server buffer has been marked as active. | ||
| 1515 | (erc-display-error-notice | ||
| 1516 | nil (concat m " See Info:\"(erc) Required Modules\" for more.")) | ||
| 1517 | (lwarn 'erc :warning m)))) | ||
| 1518 | |||
| 1505 | (defun erc-ports-list (ports) | 1519 | (defun erc-ports-list (ports) |
| 1506 | "Return a list of PORTS. | 1520 | "Return a list of PORTS. |
| 1507 | 1521 | ||
diff --git a/lisp/erc/erc-sasl.el b/lisp/erc/erc-sasl.el index 78d02a46381..23110d74b5e 100644 --- a/lisp/erc/erc-sasl.el +++ b/lisp/erc/erc-sasl.el | |||
| @@ -435,7 +435,7 @@ Otherwise, expect it to disappear in subsequent versions.") | |||
| 435 | (if (eq :user (alist-get 'user erc-sasl--options)) | 435 | (if (eq :user (alist-get 'user erc-sasl--options)) |
| 436 | (erc-current-nick) | 436 | (erc-current-nick) |
| 437 | erc-session-username))) | 437 | erc-session-username))) |
| 438 | (erc-login)) | 438 | (cl-call-next-method)) |
| 439 | (when erc-sasl--send-cap-ls | 439 | (when erc-sasl--send-cap-ls |
| 440 | (erc-server-send "CAP REQ :sasl")) | 440 | (erc-server-send "CAP REQ :sasl")) |
| 441 | (erc-server-send (format "AUTHENTICATE %s" m))) | 441 | (erc-server-send (format "AUTHENTICATE %s" m))) |
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 6a5e0018964..16a0aba77b1 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el | |||
| @@ -1607,7 +1607,8 @@ same manner." | |||
| 1607 | (when target ; compat | 1607 | (when target ; compat |
| 1608 | (setq tgt-info (erc--target-from-string target))) | 1608 | (setq tgt-info (erc--target-from-string target))) |
| 1609 | (if tgt-info | 1609 | (if tgt-info |
| 1610 | (let* ((esid (erc-networks--id-symbol erc-networks--id)) | 1610 | (let* ((esid (and erc-networks--id |
| 1611 | (erc-networks--id-symbol erc-networks--id))) | ||
| 1611 | (name (if esid | 1612 | (name (if esid |
| 1612 | (erc-networks--reconcile-buffer-names tgt-info | 1613 | (erc-networks--reconcile-buffer-names tgt-info |
| 1613 | erc-networks--id) | 1614 | erc-networks--id) |
| @@ -6760,7 +6761,8 @@ This should be a string with substitution variables recognized by | |||
| 6760 | If the name of the network is not available, then use the | 6761 | If the name of the network is not available, then use the |
| 6761 | shortened server name instead." | 6762 | shortened server name instead." |
| 6762 | (if-let ((erc--target) | 6763 | (if-let ((erc--target) |
| 6763 | (name (if-let ((esid (erc-networks--id-symbol erc-networks--id))) | 6764 | (name (if-let ((erc-networks--id) |
| 6765 | (esid (erc-networks--id-symbol erc-networks--id))) | ||
| 6764 | (symbol-name esid) | 6766 | (symbol-name esid) |
| 6765 | (erc-shorten-server-name (or erc-server-announced-name | 6767 | (erc-shorten-server-name (or erc-server-announced-name |
| 6766 | erc-session-server))))) | 6768 | erc-session-server))))) |
diff --git a/test/lisp/erc/erc-scenarios-base-unstable.el b/test/lisp/erc/erc-scenarios-base-unstable.el index f5b8df6f4a1..e6db40c5efb 100644 --- a/test/lisp/erc/erc-scenarios-base-unstable.el +++ b/test/lisp/erc/erc-scenarios-base-unstable.el | |||
| @@ -24,7 +24,7 @@ | |||
| 24 | (let ((load-path (cons (ert-resource-directory) load-path))) | 24 | (let ((load-path (cons (ert-resource-directory) load-path))) |
| 25 | (require 'erc-scenarios-common))) | 25 | (require 'erc-scenarios-common))) |
| 26 | 26 | ||
| 27 | (eval-when-compile (require 'erc-join)) | 27 | (eval-when-compile (require 'erc-join) (require 'warnings)) |
| 28 | 28 | ||
| 29 | ;; Not unstable, but stashed here for now | 29 | ;; Not unstable, but stashed here for now |
| 30 | 30 | ||
| @@ -132,4 +132,56 @@ | |||
| 132 | (not (setq failed (zerop (cl-decf tries))))))) | 132 | (not (setq failed (zerop (cl-decf tries))))))) |
| 133 | (should-not failed))) | 133 | (should-not failed))) |
| 134 | 134 | ||
| 135 | ;; The `erc-networks' library has slowly become a hard dependency of | ||
| 136 | ;; the interactive client since its incorporation in 2006. But its | ||
| 137 | ;; module, which was added in ERC 5.3 (2008) and thereafter loaded by | ||
| 138 | ;; default, only became quasi-required in ERC 5.5 (2022). Despite | ||
| 139 | ;; this, a basic connection should still always succeed, at least long | ||
| 140 | ;; enough to warn users that their setup is abnormal. Of course, | ||
| 141 | ;; third-party code intentionally omitting the module will have to | ||
| 142 | ;; override various erc-server-*-functions to avoid operating in a | ||
| 143 | ;; degraded state, which has likely been the case for a while. | ||
| 144 | |||
| 145 | (ert-deftest erc-scenarios-networks-no-module () | ||
| 146 | :tags '(:expensive-test) | ||
| 147 | (erc-scenarios-common-with-cleanup | ||
| 148 | ((erc-scenarios-common-dialog "networks/no-module") | ||
| 149 | (erc-server-flood-penalty 0.1) | ||
| 150 | (erc-networks-mode-orig erc-networks-mode) | ||
| 151 | (dumb-server (erc-d-run "localhost" t 'basic)) | ||
| 152 | (port (process-contact dumb-server :service)) | ||
| 153 | (erc-modules (remq 'networks erc-modules)) | ||
| 154 | (warning-suppress-log-types '((erc))) | ||
| 155 | (expect (erc-d-t-make-expecter))) | ||
| 156 | |||
| 157 | (erc-networks-mode -1) | ||
| 158 | (ert-info ("Connect and retain dialed name") | ||
| 159 | (with-current-buffer (erc :server "127.0.0.1" | ||
| 160 | :port port | ||
| 161 | :nick "tester" | ||
| 162 | :user "tester" | ||
| 163 | :full-name "tester") | ||
| 164 | (funcall expect 10 "Required module `networks' not loaded") | ||
| 165 | (funcall expect 10 "This server is in debug mode") | ||
| 166 | ;; Buffer not named after network | ||
| 167 | (should (string= (buffer-name) (format "127.0.0.1:%d" port))) | ||
| 168 | (erc-cmd-JOIN "#chan"))) | ||
| 169 | |||
| 170 | (ert-info ("Join #chan, change nick, query op") | ||
| 171 | (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) | ||
| 172 | (funcall expect 20 "Even at thy teat thou") | ||
| 173 | (erc-cmd-NICK "dummy") | ||
| 174 | (funcall expect 10 "Your new nickname is dummy") | ||
| 175 | (erc-scenarios-common-say "/msg alice hi"))) | ||
| 176 | |||
| 177 | (ert-info ("Switch to query and quit") | ||
| 178 | (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "alice")) | ||
| 179 | (funcall expect 20 "bye")) | ||
| 180 | |||
| 181 | (with-current-buffer (format "127.0.0.1:%d" port) | ||
| 182 | (erc-cmd-QUIT "") | ||
| 183 | (funcall expect 10 "finished"))) | ||
| 184 | (when erc-networks-mode-orig | ||
| 185 | (erc-networks-mode +1)))) | ||
| 186 | |||
| 135 | ;;; erc-scenarios-base-unstable.el ends here | 187 | ;;; erc-scenarios-base-unstable.el ends here |
diff --git a/test/lisp/erc/resources/networks/no-module/basic.eld b/test/lisp/erc/resources/networks/no-module/basic.eld new file mode 100644 index 00000000000..f1bdbd1219f --- /dev/null +++ b/test/lisp/erc/resources/networks/no-module/basic.eld | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | ;; -*- mode: lisp-data; -*- | ||
| 2 | ((nick 10 "NICK tester")) | ||
| 3 | ((user 1 "USER tester 0 * :tester") | ||
| 4 | (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") | ||
| 5 | (0.00 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.8.0") | ||
| 6 | (0.00 ":irc.foonet.org 003 tester :This server was created Mon, 12 Dec 2022 01:25:38 UTC") | ||
| 7 | (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.8.0 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") | ||
| 8 | (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") | ||
| 9 | (0.00 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") | ||
| 10 | (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") | ||
| 11 | (0.00 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") | ||
| 12 | (0.00 ":irc.foonet.org 252 tester 0 :IRC Operators online") | ||
| 13 | (0.00 ":irc.foonet.org 253 tester 0 :unregistered connections") | ||
| 14 | (0.00 ":irc.foonet.org 254 tester 1 :channels formed") | ||
| 15 | (0.00 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") | ||
| 16 | (0.00 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") | ||
| 17 | (0.01 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") | ||
| 18 | (0.00 ":irc.foonet.org 422 tester :MOTD File is missing")) | ||
| 19 | |||
| 20 | ((mode 10 "MODE tester +i") | ||
| 21 | (0.00 ":irc.foonet.org 221 tester +i") | ||
| 22 | (0.00 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) | ||
| 23 | |||
| 24 | ((join 10 "JOIN #chan") | ||
| 25 | (0.03 ":tester!~u@z5d6jyn8pwxge.irc JOIN #chan")) | ||
| 26 | |||
| 27 | ((~nick 10 "NICK dummy") | ||
| 28 | (0.01 ":tester!~u@z5d6jyn8pwxge.irc NICK dummy")) | ||
| 29 | |||
| 30 | ((mode-1 10 "MODE #chan") | ||
| 31 | (0.01 ":irc.foonet.org 353 tester = #chan :@alice bob foonet tester") | ||
| 32 | (0.00 ":irc.foonet.org 366 tester #chan :End of NAMES list") | ||
| 33 | (0.03 ":irc.foonet.org 324 tester #chan +nt") | ||
| 34 | (0.00 ":irc.foonet.org 329 tester #chan 1670808354") | ||
| 35 | (0.00 ":bob!~u@d6ftaiqzk8x2k.irc PRIVMSG #chan :tester, welcome!") | ||
| 36 | (0.00 ":alice!~u@d6ftaiqzk8x2k.irc PRIVMSG #chan :tester, welcome!") | ||
| 37 | (0.03 ":bob!~u@d6ftaiqzk8x2k.irc PRIVMSG #chan :alice: Forbear it therefore; give your cause to heaven.") | ||
| 38 | (0.01 ":alice!~u@d6ftaiqzk8x2k.irc PRIVMSG #chan :bob: Even at thy teat thou hadst thy tyranny.")) | ||
| 39 | |||
| 40 | ((privmsg 10 "PRIVMSG alice :hi") | ||
| 41 | (0.00 ":alice!~u@d6ftaiqzk8x2k.irc PRIVMSG dummy :bye")) | ||
| 42 | |||
| 43 | ((quit 10 "QUIT :\2ERC\2") | ||
| 44 | (0.03 ":dummy!~u@z5d6jyn8pwxge.irc QUIT :Quit: \2ERC\2")) | ||