diff options
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/circe-20180525.1231/irc.el')
-rw-r--r-- | configs/shared/emacs/.emacs.d/elpa/circe-20180525.1231/irc.el | 1413 |
1 files changed, 0 insertions, 1413 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/circe-20180525.1231/irc.el b/configs/shared/emacs/.emacs.d/elpa/circe-20180525.1231/irc.el deleted file mode 100644 index 04b260c75a43..000000000000 --- a/configs/shared/emacs/.emacs.d/elpa/circe-20180525.1231/irc.el +++ /dev/null @@ -1,1413 +0,0 @@ -;;; irc.el --- Library to handle IRC connections -*- lexical-binding: t -*- - -;; Copyright (C) 2015 Jorgen Schaefer <contact@jorgenschaefer.de> - -;; Author: Jorgen Schaefer <contact@jorgenschaefer.de> -;; URL: https://github.com/jorgenschaefer/circe - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <http://www.gnu.org/licenses/>. - -;;; Commentary: - -;; The main entry function is `irc-connect'. This creates a new -;; connection to an IRC server, and also takes an event handler table -;; which is used to run various event handlers. Handlers receive a -;; connection object which can be used for other API calls. - -;; IRC connection objects also accept connection options. These can be -;; queried using `irc-connection-get', and are set by `irc-connect' or -;; later using `irc-connection-put'. - -;; Event handler tables are simple maps of names to functions. See -;; `irc-handler-table', `irc-handler-add' and `irc-handler-run' for -;; the API. - -;; To send commands to the server, use `irc-send-raw' or -;; `irc-send-command'. - -;; The rest of the library are handler packs that add support for -;; various IRC features. - -;;; Code: - -(require 'cl-lib) -(require 'make-tls-process) - -(defvar irc-debug-log nil - "Emit protocol debug info if this is non-nil.") - -;;;;;;;;;;;;;;;;;;;;;;; -;;; Connection function - -(defun irc-connect (&rest keywords) - "Connect to an IRC server. - -Supported keyword arguments: - -:name NAME -- The name for the process -:host HOST -- The host to connect to -:service SERVICE -- The service or port to connect to -:tls BOOL -- Whether to use TLS -:family IP-FAMILY -- Force using of ipv4 or ipv6 -:handler-table HANDLER -- The event handler table to send events to. - -The following events are supported: - -conn.connected conn -- The connection was established -conn.failed conn -- The connection could not be established -conn.disconnected conn -- A previously established connection was lost - -NNN conn sender args... -- A numeric reply from IRC was received -COMMAND conn sender args... -- An IRC command message was received" - (let ((proc (funcall (if (plist-get keywords :tls) - #'make-tls-process - #'make-network-process) - :name (or (plist-get keywords :name) - (plist-get keywords :host)) - :host (or (plist-get keywords :host) - (error "Must specify a :host to connect to")) - :service (or (plist-get keywords :service) - (error "Must specify a :service to connect to")) - :family (plist-get keywords :family) - :coding 'no-conversion - :nowait (featurep 'make-network-process '(:nowait t)) - :noquery t - :filter #'irc--filter - :sentinel #'irc--sentinel - :plist keywords - :keepalive t))) - ;; When we used `make-network-process' without :nowait, the - ;; sentinel is not called with the open event, so we do this - ;; manually. - (when (eq (process-status proc) 'open) - (irc--sentinel proc "open manually")) - proc)) - -(defun irc-connection-get (conn propname) - "Return the value of CONN's PROPNAME property." - (process-get conn propname)) - -(defun irc-connection-put (conn propname value) - "Change CONN's PROPNAME property to VALUE." - (process-put conn propname value)) - -(defun irc--sentinel (proc event) - (cond - ((string-match "\\`failed" event) - (irc-event-emit proc "conn.failed")) - ((string-match "\\`open" event) - (irc-event-emit proc "conn.connected")) - ((string-match "\\`\\(connection broken\\|finished\\|exited abnormally\\)" - event) - (irc-event-emit proc "conn.disconnected")) - ((string-match "\\`\\(deleted\\|killed\\)" event) - nil) - (t - (error "Unknown event in IRC sentinel: %S" event)))) - -(defvar irc--filter-running-p nil - "Non-nil when we're currently processing a message. - -Yep, this is a mutex. Why would one need a mutex in Emacs, a -single-threaded application, you ask? Easy! - -When, during the execution of a process filter, any piece of code -waits for process output - e.g. because they started a some -external program - Emacs will process any input from external -processes. Including the one for the filter that is currently -running. - -If that process does emit output, the filter is run again, while -it is already running. If the filter is not careful, this can -cause data to arrive out of order, or get lost.") - -(defun irc--filter (proc data) - "Handle data from the process." - (irc-connection-put proc :conn-data - (concat (or (irc-connection-get proc :conn-data) - "") - data)) - (when (not irc--filter-running-p) - (let ((irc--filter-running-p t) - (data (irc-connection-get proc :conn-data))) - (while (string-match "\r?\n" data) - (let ((line (substring data 0 (match-beginning 0)))) - (setq data (substring data (match-end 0))) - (irc-connection-put proc :conn-data data) - (irc--handle-line proc line) - (setq data (irc-connection-get proc :conn-data))))))) - -(defun irc--handle-line (proc line) - "Handle a single line from the IRC server. - -The command is simply passed to the event handler of the IRC -connection." - (irc-debug-out proc "S: %s" line) - (let* ((parsed (irc--parse line)) - (sender (car parsed)) - (command (cadr parsed)) - (args (cddr parsed))) - (apply #'irc-event-emit proc command sender args))) - -(defun irc--parse (line) - "Parse a line from IRC. - -Returns a list: (sender command args...) - -A line from IRC is a space-separated list of arguments. If the -first word starts with a colon, that's the sender. The first or -second word is the command. All further words are arguments. The -first word to start with a colon ends the argument list. - -Examples: - -COMMAND -COMMAND arg -COMMAND arg1 arg2 -COMMAND arg1 arg2 :arg3 still arg3 -:sender COMMAND arg1 arg2 :arg3 still arg3" - (with-temp-buffer - (insert line) - (goto-char (point-min)) - (let ((sender nil) - (args nil)) - ;; Optional sender. - (when (looking-at ":\\([^ ]+\\) ") - (setq sender (decode-coding-string - (match-string 1) - 'undecided)) - (goto-char (match-end 0))) - - ;; COMMAND. - (unless (looking-at "\\([^ ]+\\)") - (error "Invalid message: %s" line)) - (push (decode-coding-string (match-string 1) 'undecided) - args) - (goto-char (match-end 0)) - - ;; Arguments. - (while (re-search-forward " :\\(.*\\)\\| \\([^ ]*\\)" nil t) - (push (decode-coding-string - (or (match-string 1) - (match-string 2)) - 'undecided) - args)) - - (cons sender (nreverse args))))) - -(defun irc-userstring-nick (userstring) - "Return the nick in a given USERSTRING. - -USERSTRING is a typical nick!user@host prefix as used by IRC." - (if (string-match "\\`\\([^!]+\\)!\\([^@]+\\)@\\(.*\\)\\'" userstring) - (match-string 1 userstring) - userstring)) - -(defun irc-userstring-userhost (userstring) - "Return the nick in a given USERSTRING. - -USERSTRING is a typical nick!user@host prefix as used by IRC." - (if (string-match "\\`\\([^!]+\\)!\\([^@]+@.*\\)\\'" userstring) - (match-string 2 userstring) - nil)) - -(defun irc-event-emit (conn event &rest args) - "Run the event handlers for EVENT in CONN with ARGS." - (irc-debug-out conn - "E: %S %s" - event - (mapconcat (lambda (elt) (format "%S" elt)) - args - " ")) - (let ((handler-table (irc-connection-get conn :handler-table))) - (when handler-table - (apply #'irc-handler-run handler-table event conn event args) - (apply #'irc-handler-run handler-table nil conn event args)))) - -;;;;;;;;;;;;;;;;;;;;;;; -;;; Event handler table - -(defun irc-handler-table () - "Return a new event handler table." - (make-hash-table :test 'equal)) - -(defun irc-handler-add (table event handler) - "Add HANDLER for EVENT to the event handler table TABLE." - (puthash event - (append (gethash event table) - (list handler)) - table)) - -(defun irc-handler-remove (table event handler) - "Remove HANDLER for EVENT to the event handler table TABLE." - (puthash event - (delete handler - (gethash event table)) - table)) - -(defun irc-handler-run (table event &rest args) - "Run the handlers for EVENT in TABLE, passing ARGS to each." - (dolist (handler (gethash event table)) - (if debug-on-error - (apply handler args) - (condition-case err - (apply handler args) - (error - (message "Error running event %S handler %S: %S (args were %S)" - event handler err args)))))) - -;;;;;;;;;;; -;;; Sending - -(defun irc-send-raw (conn line &optional flood-handling) - "Send a line LINE to the IRC connection CONN. - -LINE should not include the trailing newline. - -FLOOD-HANDLING defines how to handle the situation when we are -sending too much data. It can have three values: - -nil -- Add the message to a queue and send it later -:nowait -- Send the message immediately, circumventing flood protection -:drop -- Send the message only if we are not flooding, and drop it if - we have queued up messages. - -The flood protection algorithm works like the one detailed in RFC -2813, section 5.8 \"Flood control of clients\". - - * If `flood-last-message' is less than the current - time, set it equal. - * While `flood-last-message' is less than `flood-margin' - seconds ahead of the current time, send a message, and - increase `flood-last-message' by `flood-penalty'." - (cond - ((null flood-handling) - (irc-connection-put conn - :flood-queue - (append (irc-connection-get conn :flood-queue) - (list line))) - (irc-send--queue conn)) - ((eq flood-handling :nowait) - (irc-send--internal conn line)) - ((eq flood-handling :drop) - (let ((queue (irc-connection-get conn :flood-queue))) - (when (not queue) - (irc-connection-put conn :flood-queue (list line)) - (irc-send--queue conn)))))) - -(defun irc-send--queue (conn) - "Send messages from the flood queue in CONN. - -See `irc-send-raw' for the algorithm." - (let ((queue (irc-connection-get conn :flood-queue)) - (last-message (or (irc-connection-get conn :flood-last-message) - 0)) - (margin (or (irc-connection-get conn :flood-margin) - 10)) - (penalty (or (irc-connection-get conn :flood-penalty) - 3)) - (now (float-time))) - (when (< last-message now) - (setq last-message now)) - (while (and queue - (< last-message (+ now margin))) - (irc-send--internal conn (car queue)) - (setq queue (cdr queue) - last-message (+ last-message penalty))) - (irc-connection-put conn :flood-queue queue) - (irc-connection-put conn :flood-last-message last-message) - (let ((timer (irc-connection-get conn :flood-timer))) - (when timer - (cancel-timer timer) - (irc-connection-put conn :flood-timer nil)) - (when queue - (irc-connection-put conn - :flood-timer - (run-at-time 1 nil #'irc-send--queue conn)))))) - -(defun irc-send--internal (conn line) - "Send LINE to CONN." - (irc-debug-out conn "C: %s" line) - (process-send-string conn - (concat (encode-coding-string line 'utf-8) - "\r\n"))) - -(defun irc-send-command (conn command &rest args) - "Send COMMAND with ARGS to IRC connection CONN." - (irc-send-raw conn (apply #'irc--format-command command args))) - -(defun irc--format-command (command &rest args) - "Format COMMAND and ARGS for IRC. - -The last value in ARGS will be escaped with a leading colon if it -contains a space. All other arguments are checked to make sure -they do not contain a space." - (dolist (arg (cons command args)) - (when (not (stringp arg)) - (error "Argument must be a string"))) - (let* ((prefix (cons command (butlast args))) - (last (last args))) - (dolist (arg prefix) - (when (string-match " " arg) - (error "IRC protocol error: Argument %S must not contain space" - arg))) - (when (and last (or (string-match " " (car last)) - (string-match "^:" (car last)) - (equal "" (car last)))) - (setcar last (concat ":" (car last)))) - (mapconcat #'identity - (append prefix last) - " "))) - -(defun irc-send-AUTHENTICATE (conn arg) - "Send an AUTHENTICATE message with ARG. - -See https://github.com/atheme/charybdis/blob/master/doc/sasl.txt -for details." - (irc-send-command conn "AUTHENTICATE" arg)) - -(defun irc-send-AWAY (conn &optional reason) - "Mark yourself as AWAY with reason REASON, or back if reason is nil." - (if reason - (irc-send-command conn "AWAY" reason) - (irc-send-command conn "AWAY"))) - -(defun irc-send-CAP (conn &rest args) - "Send a CAP message. - -See https://tools.ietf.org/html/draft-mitchell-irc-capabilities-01 -for details." - (apply #'irc-send-command conn "CAP" args)) - -(defun irc-send-INVITE (conn nick channel) - "Invite NICK to CHANNEL." - (irc-send-command conn "INVITE" nick channel)) - -(defun irc-send-JOIN (conn channel &optional key) - "Join CHANNEL. - -If KEY is given, use it to join the password-protected channel." - (if key - (irc-send-command conn "JOIN" channel key) - (irc-send-command conn "JOIN" channel))) - -(defun irc-send-NAMES (conn &optional channel) - "Retrieve user names from the server, optionally limited to CHANNEL." - (if channel - (irc-send-command conn "NAMES" channel) - (irc-send-command conn "NAMES"))) - -(defun irc-send-NICK (conn nick) - "Change your own nick to NICK." - (irc-send-command conn "NICK" nick)) - -(defun irc-send-NOTICE (conn msgtarget text-to-be-sent) - "Send a private notice containing TEXT-TO-BE-SENT to MSGTARGET. - -MSGTARGET can be either a nick or a channel." - (irc-send-command conn "NOTICE" msgtarget text-to-be-sent)) - -(defun irc-send-PART (conn channel reason) - "Leave CHANNEL with reason REASON." - (irc-send-command conn "PART" channel reason)) - -(defun irc-send-PASS (conn password) - "Authenticate to the server using PASSWORD." - (irc-send-command conn "PASS" password)) - -(defun irc-send-PONG (conn server) - "Respond to a PING message." - (irc-send-raw conn - (irc--format-command "PONG" server) - :nowait)) - -(defun irc-send-PRIVMSG (conn msgtarget text-to-be-sent) - "Send a private message containing TEXT-TO-BE-SENT to MSGTARGET. - -MSGTARGET can be either a nick or a channel." - (irc-send-command conn "PRIVMSG" msgtarget text-to-be-sent)) - -(defun irc-send-QUIT (conn reason) - "Leave IRC with reason REASON." - (irc-send-command conn "QUIT" reason)) - -(defun irc-send-TOPIC (conn channel &optional new-topic) - "Retrieve or set the topic of CHANNEL - -If NEW-TOPIC is given, set this as the new topic. If it is -omitted, retrieve the current topic." - (if new-topic - (irc-send-command conn "TOPIC" channel new-topic) - (irc-send-command conn "TOPIC" channel))) - -(defun irc-send-USER (conn user mode realname) - "Send a USER message for registration. - -MODE should be an integer as per RFC 2812" - (irc-send-command conn "USER" user (format "%s" mode) "*" realname)) - -(defun irc-send-WHOIS (conn target &optional server-or-name) - "Retrieve current whois information on TARGET." - (if server-or-name - (irc-send-command conn "WHOIS" target server-or-name) - (irc-send-command conn "WHOIS" target))) - -(defun irc-send-WHOWAS (conn target) - "Retrieve past whois information on TARGET." - (irc-send-command conn "WHOWAS" target)) - -(defun irc-send-STATS (conn query &optional server) - "Return statistics on current server, or SERVER if it is specified." - (if server - (irc-send-command conn "STATS" query server) - (irc-send-command conn "STATS" query))) - -;;;;;;;;;;;;;;; -;;; Debug stuff - -(defun irc-debug-out (conn fmt &rest args) - (when irc-debug-log - (let ((name (format "*IRC Protocol %s:%s*" - (irc-connection-get conn :host) - (irc-connection-get conn :service)))) - (with-current-buffer (get-buffer-create name) - (save-excursion - (goto-char (point-max)) - (insert (apply #'format fmt args) "\n")))))) - -;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Handler: Registration - -(defun irc-handle-registration (table) - "Add command handlers to TABLE to handle registration. - -This will send the usual startup messages after we are connected. - -Events emitted: - -\"irc.registered\" current-nick -- We have successfully - registered with the IRC server. Most commands can be used now. - In particular, joining channels is only possible now. - -\"sasl.login\" nick!user@host account -- SASL log in was - successful. - -Connection options used: - -:nick -- The nick to use to register with the server -:user -- The user name to use -:mode -- The initial mode to use; an integer. See RFC 2812 for - the meaning. -:realname -- The realname to use for the registration -:pass -- The server password to send -:cap-req -- CAP protocol capabilities to request, if available -:sasl-username -- The SASL username to send, if sasl is available -:sasl-password -- The SASL password to send, if sasl is available - -Connection options set: - -:connection-state -- One of nil, connected, registered, disconnected - See `irc-connection-state' for an interface to this. -:cap-supported-p -- Non-nil if the server supports the CAP protocol -:cap-ack -- The list of active capabilities negotiated with the server" - (irc-handler-add table "conn.connected" - #'irc-handle-registration--connected) - (irc-handler-add table "conn.disconnected" - #'irc-handle-registration--disconnected) - (irc-handler-add table "001" ;; RPL_WELCOME - #'irc-handle-registration--rpl-welcome) - (irc-handler-add table "CAP" - #'irc-handle-registration--cap) - (irc-handler-add table "AUTHENTICATE" - #'irc-handle-registration--authenticate) - (irc-handler-add table "900" ;; RPL_LOGGEDIN - #'irc-handle-registration--logged-in)) - -(defun irc-handle-registration--connected (conn _event) - (irc-connection-put conn :connection-state 'connected) - (when (irc-connection-get conn :cap-req) - (irc-send-CAP conn "LS")) - (let ((password (irc-connection-get conn :pass))) - (when password - (irc-send-PASS conn password))) - (irc-send-NICK conn (irc-connection-get conn :nick)) - (irc-send-USER conn - (irc-connection-get conn :user) - (irc-connection-get conn :mode) - (irc-connection-get conn :realname))) - -(defun irc-handle-registration--disconnected (conn _event) - (irc-connection-put conn :connection-state 'disconnected)) - -(defun irc-handle-registration--rpl-welcome (conn _event _sender target - &rest ignored) - (irc-connection-put conn :connection-state 'registered) - (irc-event-emit conn "irc.registered" target)) - -(defun irc-handle-registration--cap (conn _event _sender _target - subcommand arg) - (cond - ((equal subcommand "LS") - (let ((supported (split-string arg)) - (wanted nil)) - (dolist (cap (irc-connection-get conn :cap-req)) - (when (member cap supported) - (setq wanted (append wanted (list cap))))) - (if wanted - (irc-send-CAP conn "REQ" (mapconcat #'identity wanted " ")) - (irc-send-CAP conn "END")))) - ((equal subcommand "ACK") - (let ((acked (split-string arg))) - (irc-connection-put conn :cap-ack acked) - (if (and (member "sasl" acked) - (irc-connection-get conn :sasl-username) - (irc-connection-get conn :sasl-password)) - (irc-send-AUTHENTICATE conn "PLAIN") - (irc-send-CAP conn "END")))) - (t - (message "Unknown CAP response from server: %s %s" subcommand arg)))) - -(defun irc-handle-registration--authenticate (conn _event _sender arg) - (if (equal arg "+") - (let ((username (irc-connection-get conn :sasl-username)) - (password (irc-connection-get conn :sasl-password))) - (irc-send-AUTHENTICATE conn (base64-encode-string - (format "%s\x00%s\x00%s" - username username password))) - (irc-send-CAP conn "END")) - (message "Unknown AUTHENTICATE response from server: %s" arg))) - -(defun irc-handle-registration--logged-in (conn _event _sender _target - userhost account _message) - (irc-event-emit conn "sasl.login" userhost account)) - -(defun irc-connection-state (conn) - "connecting connected registered disconnected" - (let ((state (irc-connection-get conn :connection-state))) - (if (null state) - 'connecting - state))) - -;;;;;;;;;;;;;;;;;;;;;; -;;; Handler: Ping-Pong - -(defun irc-handle-ping-pong (table) - "Add command handlers to respond to PING requests." - (irc-handler-add table "PING" #'irc-handle-ping-pong--ping)) - -(defun irc-handle-ping-pong--ping (conn _event _sender argument) - (irc-send-PONG conn argument)) - -;;;;;;;;;;;;;;;;;;;;; -;;; Handler: ISUPPORT - -(defun irc-handle-isupport (table) - "Add command handlers to track 005 RPL_ISUPPORT capabilities." - (irc-handler-add table "005" #'irc-handle-isupport--005)) - -(defun irc-handle-isupport--005 (conn _event _sender _target &rest args) - (irc-connection-put - conn :isupport - (append (irc-connection-get conn :isupport) - (irc-handle-isupport--capabilities-to-alist args)))) - -(defun irc-handle-isupport--capabilities-to-alist (capabilities) - (mapcar (lambda (cap) - (if (string-match "\\`\\([^=]+\\)=\\(.*\\)\\'" cap) - (cons (match-string 1 cap) - (match-string 2 cap)) - (cons cap t))) - capabilities)) - -(defun irc-isupport (conn capability) - "Return the value of CAPABILITY of CONN. - -These capabilities are set when the server sends a 005 -RPL_ISUPPORT message. The return value is either the value of the -capability, or t if it is a boolean capability that is present. -If the capability is not present, the return value is nil." - (cdr (assoc capability - (irc-connection-get conn :isupport)))) - -(defun irc-string-equal-p (conn s1 s2) - "Compare S1 to S2 case-insensitively. - -What case means is defined by the server of CONN." - (equal (irc-isupport--case-fold conn s1) - (irc-isupport--case-fold conn s2))) - -(defvar irc-isupport--ascii-table - (let ((table (make-string 128 0)) - (char 0)) - (while (<= char 127) - (if (and (<= ?A char) - (<= char ?Z)) - (aset table char (+ char (- ?a ?A))) - (aset table char char)) - (setq char (1+ char))) - table) - "A case mapping table for the ascii CASEMAPPING.") - -(defvar irc-isupport--rfc1459-table - (let ((table (concat irc-isupport--ascii-table))) ; copy string - (aset table ?\[ ?\{) - (aset table ?\] ?\}) - (aset table ?\\ ?\|) - (aset table ?^ ?\~) - table) - "A case mapping table for the rfc1459 CASEMAPPING.") - -(defvar irc-isupport--rfc1459-strict-table - (let ((table (concat irc-isupport--ascii-table))) ; copy string - (aset table ?\[ ?\{) - (aset table ?\] ?\}) - (aset table ?\\ ?\|) - table) - "A case mapping table for the rfc1459-strict CASEMAPPING.") - -(defun irc-isupport--case-fold (conn s) - "Translate S to be a lower-case. - -This uses the case mapping defined by the IRC server for CONN." - (with-temp-buffer - (insert s) - (let ((mapping (or (irc-isupport conn "CASEMAPPING") - "rfc1459"))) - (cond - ((equal mapping "rfc1459") - (translate-region (point-min) - (point-max) - irc-isupport--rfc1459-table)) - ((equal mapping "ascii") - (translate-region (point-min) - (point-max) - irc-isupport--ascii-table)) - ((equal mapping "rfc1459-strict") - (translate-region (point-min) - (point-max) - irc-isupport--rfc1459-strict-table)))) - (buffer-string))) - -(defun irc-channel-name-p (conn string) - "True iff STRING is a valid channel name for CONN. - -This depends on the CHANTYPES setting set by the server of CONN." - (let ((chantypes (string-to-list - (or (irc-isupport conn "CHANTYPES") - "#")))) - (if (and (> (length string) 0) - (member (aref string 0) chantypes)) - t - nil))) - -(defun irc-nick-without-prefix (conn nick) - "Return NICK without any mode prefixes. - -For example, a user with op status might be shown as @Nick. This -function would return Nick without the prefix. This uses the 005 -RPL_ISUPPORT setting of PREFIX set by the IRC server for CONN." - (let ((prefixes (irc-connection-get conn :nick-prefixes))) - (when (not prefixes) - (let ((prefix-string (or (irc-isupport conn "PREFIX") - "(qaohv)~&@%+"))) - (setq prefixes (string-to-list - (if (string-match "(.*)\\(.*\\)" prefix-string) - (match-string 1 prefix-string) - "~&@%+"))) - (irc-connection-put conn :nick-prefixes prefixes))) - (while (and (> (length nick) 0) - (member (aref nick 0) prefixes)) - (setq nick (substring nick 1))) - nick)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Handler: Initial nick acquisition - -(defun irc-handle-initial-nick-acquisition (table) - "Track the current nick of the user. - -Connection options used: - -:nick-alternatives -- A list of nicks to try if the first attempt - does not succeed." - (irc-handler-add table "432" ;; ERR_ERRONEUSNICKNAME - #'irc-handle-initial-nick-acquisition--get-initial-nick) - (irc-handler-add table "433" ;; ERR_NICKNAMEINUSE - #'irc-handle-initial-nick-acquisition--get-initial-nick) - (irc-handler-add table "437" ;; ERR_UNAVAILRESOURCE - #'irc-handle-initial-nick-acquisition--get-initial-nick)) - -(defun irc-handle-initial-nick-acquisition--get-initial-nick - (conn _event _sender current-nick _attempted-nick _reason) - (when (equal current-nick "*") - (let ((alternatives (irc-connection-get conn :nick-alternatives))) - (if (not alternatives) - (irc-send-NICK conn (irc-generate-nick)) - (irc-connection-put conn :nick-alternatives (cdr alternatives)) - (irc-send-NICK conn (car alternatives)))))) - -(defun irc-generate-nick () - "Return a random, valid IRC nick name. - -Valid nick names are at least (RFC 1459): - -<nick> ::= <letter> { <letter> | <number> | <special> } -<special> ::= '-' | '[' | ']' | '\' | '`' | '^' | '{' | '}'" - (let ((chars "abcdefghijklmnopqrstuvwxyz")) - (mapconcat (lambda (_) - (make-string 1 (aref chars (random (length chars))))) - (make-string 9 0) - ""))) - -;;;;;;;;;;;;;;;;; -;;; Handler: CTCP - -(defun irc-handle-ctcp (table) - "Add command handlers to TABLE to handle the CTCP protocol. - -Connection options used: - -:ctcp-version -- The response to a CTCP VERSION request. -:ctcp-clientinfo -- The response to a CTCP CLIENTINFO request. -:ctcp-source -- The response to a CTCP SOURCE request. - -Events emitted: - -\"irc.message\" sender target body -- A non-CTCP PRIVMSG -\"irc.notice\" sender target body -- A non-CTCP NOTICE -\"irc.ctcp\" sender target verb argument -- A CTCP request. ARGUMENT - can be nil if there was no argument, or the empty string if the - argument was empty. -\"irc.ctcpreply\" sender target verb argument -- A CTCP reply. - ARGUMENT is similar to above. -\"irc.ctcp.VERB\" sender target argument -- A CTCP request of - this specific type. -\"irc.ctcpreply.VERB\" sender target argument -- A CTCP reply of - this specific type." - (irc-handler-add table "PRIVMSG" - #'irc-handle-ctcp--privmsg) - (irc-handler-add table "irc.ctcp" - #'irc-handle-ctcp--ctcp) - (irc-handler-add table "NOTICE" - #'irc-handle-ctcp--notice) - (irc-handler-add table "irc.ctcpreply" - #'irc-handle-ctcp--ctcpreply) - (irc-handler-add table "irc.ctcp.VERSION" - #'irc-handle-ctcp--ctcp-version) - (irc-handler-add table "irc.ctcp.CLIENTINFO" - #'irc-handle-ctcp--ctcp-clientinfo) - (irc-handler-add table "irc.ctcp.SOURCE" - #'irc-handle-ctcp--ctcp-source) - (irc-handler-add table "irc.ctcp.PING" - #'irc-handle-ctcp--ctcp-ping) - (irc-handler-add table "irc.ctcp.TIME" - #'irc-handle-ctcp--ctcp-time) - ) - -(defun irc-handle-ctcp--privmsg (conn _event sender target body) - (if (string-match "\\`\x01\\([^ ]+\\)\\(?: \\(.*\\)\\)?\x01\\'" - body) - (irc-event-emit conn "irc.ctcp" sender target - (match-string 1 body) - (match-string 2 body)) - (irc-event-emit conn "irc.message" sender target body))) - -(defun irc-handle-ctcp--ctcp (conn _event sender target verb argument) - (irc-event-emit conn - (format "irc.ctcp.%s" (upcase verb)) - sender - target - argument)) - -(defun irc-handle-ctcp--notice (conn _event sender target body) - (if (string-match "\\`\x01\\([^ ]+\\)\\(?: \\(.*\\)\\)?\x01\\'" - body) - (irc-event-emit conn "irc.ctcpreply" sender target - (match-string 1 body) - (match-string 2 body)) - (irc-event-emit conn "irc.notice" sender target body))) - -(defun irc-handle-ctcp--ctcpreply (conn _event sender target verb argument) - (irc-event-emit conn - (format "irc.ctcpreply.%s" (upcase verb)) - sender - target - argument)) - -(defun irc-handle-ctcp--ctcp-version (conn _event sender _target _argument) - (let ((version (irc-connection-get conn :ctcp-version))) - (when version - (irc-send-ctcpreply conn - (irc-userstring-nick sender) - "VERSION" - version)))) - -(defun irc-handle-ctcp--ctcp-clientinfo (conn _event sender _target _argument) - (let ((clientinfo (irc-connection-get conn :ctcp-clientinfo))) - (when clientinfo - (irc-send-ctcpreply conn - (irc-userstring-nick sender) - "CLIENTINFO" - clientinfo)))) - -(defun irc-handle-ctcp--ctcp-source (conn _event sender _target _argument) - (let ((source (irc-connection-get conn :ctcp-source))) - (when source - (irc-send-ctcpreply conn - (irc-userstring-nick sender) - "SOURCE" - source)))) - -(defun irc-handle-ctcp--ctcp-ping (conn _event sender _target argument) - (when argument - (irc-send-ctcpreply conn - (irc-userstring-nick sender) - "PING" - argument))) - -(defun irc-handle-ctcp--ctcp-time (conn _event sender _target _argument) - (irc-send-ctcpreply conn - (irc-userstring-nick sender) - "TIME" - (current-time-string))) - -(defun irc-send-ctcp (conn target verb &optional argument) - "Send a CTCP VERB request to TARGET, optionally with ARGUMENT." - (irc-send-PRIVMSG conn - target - (format "\x01%s%s\x01" - verb - (if argument - (concat " " argument) - "")))) - -(defun irc-send-ctcpreply (conn target verb &optional argument) - "Send a CTCP VERB reply to TARGET, optionally with ARGUMENT." - (irc-send-raw conn - (irc--format-command "NOTICE" - target - (format "\x01%s%s\x01" - verb - (if argument - (concat " " argument) - ""))) - :drop)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Handler: State tracking - -(defun irc-handle-state-tracking (table) - "Add command handlers to TABLE to track the IRC state. - -Connection options used: - -:current-nick -- The current nick, or nil if not known/set yet. - -Use helper functions to access the information tracked by this -handler: - -- `irc-current-nick' -- `irc-current-nick-p' - -Events emitted: - -\"channel.quit\" sender channel reason -- A user quit IRC and - left this channel that way." - (irc-handler-add table "001" ;; RPL_WELCOME - #'irc-handle-state-tracking--rpl-welcome) - (irc-handler-add table "JOIN" - #'irc-handle-state-tracking--JOIN) - (irc-handler-add table "PART" - #'irc-handle-state-tracking--PART) - (irc-handler-add table "KICK" - #'irc-handle-state-tracking--KICK) - (irc-handler-add table "QUIT" - #'irc-handle-state-tracking--QUIT) - (irc-handler-add table "NICK" - #'irc-handle-state-tracking--NICK) - (irc-handler-add table "PRIVMSG" - #'irc-handle-state-tracking--PRIVMSG) - (irc-handler-add table "353" ;; RPL_NAMREPLY - #'irc-handle-state-tracking--rpl-namreply) - (irc-handler-add table "366" ;; RPL_ENDOFNAMES - #'irc-handle-state-tracking--rpl-endofnames) - - (irc-handler-add table "TOPIC" - #'irc-handle-state-tracking--TOPIC) - (irc-handler-add table "331" ;; RPL_NOTOPIC - #'irc-handle-state-tracking--rpl-notopic) - (irc-handler-add table "332" ;; RPL_TOPIC - #'irc-handle-state-tracking--rpl-topic) - ) - -(cl-defstruct irc-channel - name - topic - last-topic - folded-name - users - recent-users - receiving-names - connection) - -(defun irc-channel-from-name (conn name) - "Create a new IRC channel object on CONN, named NAME." - (make-irc-channel :name name - :folded-name (irc-isupport--case-fold conn name) - :users (make-hash-table :test 'equal) - :recent-users (make-hash-table :test 'equal) - :connection conn)) - -(defun irc-connection-channel (conn channel-name) - "Return the channel object for CHANNEL-NAME on CONN." - (let ((channel-table (irc--connection-channel-table conn)) - (folded-name (irc-isupport--case-fold conn channel-name))) - (gethash folded-name channel-table))) - -(defun irc-connection-channel-list (conn) - "Return the list of channel object on CONN." - (let ((channel-list nil)) - (maphash (lambda (_folded-name channel) - (push channel channel-list)) - (irc--connection-channel-table conn)) - channel-list)) - -(defun irc-connection-add-channel (conn channel-name) - "Add CHANNEL-NAME to the channel table of CONN." - (let* ((channel-table (irc--connection-channel-table conn)) - (channel (irc-channel-from-name conn channel-name)) - (folded-name (irc-channel-folded-name channel))) - (when (not (gethash folded-name channel-table)) - (puthash folded-name channel channel-table)))) - -(defun irc-connection-remove-channel (conn channel-name) - "Remove CHANNEL-NAME from the channel table of CONN." - (let* ((channel-table (irc--connection-channel-table conn)) - (folded-name (irc-isupport--case-fold conn channel-name))) - (remhash folded-name channel-table))) - -(defun irc-current-nick (conn) - "Return the current nick on IRC connection CONN, or nil if not set yet." - (irc-connection-get conn :current-nick)) - -(defun irc-current-nick-p (conn nick) - "Return t if NICK is our current nick on IRC connection CONN." - (let ((current-nick (irc-current-nick conn))) - (if (and (stringp nick) - (stringp current-nick)) - (irc-string-equal-p conn current-nick nick) - nil))) - -(defun irc--connection-channel-table (conn) - (let ((table (irc-connection-get conn :channel-table))) - (when (not table) - (setq table (make-hash-table :test 'equal)) - (irc-connection-put conn :channel-table table)) - table)) - -(cl-defstruct irc-user - nick - folded-nick - userhost - join-time - last-activity-time - part-time - connection) - -(defun irc-user-from-userstring (conn userstring) - "Create an irc-user struct on CONN from USERSTRING. - -USERSTRING should be a s tring of the form \"nick!user@host\"." - (let ((nick (irc-userstring-nick userstring))) - (make-irc-user :nick nick - :folded-nick (irc-isupport--case-fold conn nick) - :userhost (let ((nick-len (length nick))) - (if (>= nick-len (length userstring)) - nil - (substring userstring (1+ nick-len)))) - :connection conn))) - -(defun irc-channel-user (channel nick) - "Return a user named NICK on channel CHANNEL." - (let ((user-table (irc-channel-users channel)) - (folded-nick (irc-isupport--case-fold (irc-channel-connection channel) - nick))) - (gethash folded-nick user-table))) - -(defun irc-channel-recent-user (channel nick) - "Return a recent user named NICK on channel CHANNEL." - (let ((user-table (irc-channel-recent-users channel)) - (folded-nick (irc-isupport--case-fold (irc-channel-connection channel) - nick))) - (gethash folded-nick user-table))) - -(defun irc-channel-add-user (channel userstring) - "Add USER to CHANNEL." - (let* ((user-table (irc-channel-users channel)) - (user (irc-user-from-userstring (irc-channel-connection channel) - userstring)) - (folded-nick (irc-user-folded-nick user)) - (recent-user (irc-channel-recent-user channel (irc-user-nick user)))) - (when (not (gethash folded-nick user-table)) - (when (and recent-user - (equal (irc-user-userhost recent-user) - (irc-user-userhost user))) - (setf (irc-user-last-activity-time user) - (irc-user-last-activity-time recent-user))) - (puthash folded-nick user user-table) - user))) - -(defun irc-channel-remove-user (channel nick) - "Remove NICK from CHANNEL." - (let* ((user-table (irc-channel-users channel)) - (recent-user-table (irc-channel-recent-users channel)) - (folded-nick (irc-isupport--case-fold (irc-channel-connection channel) - nick)) - (user (gethash folded-nick user-table))) - (remhash folded-nick user-table) - (when user - (setf (irc-user-part-time user) (float-time)) - (puthash folded-nick user recent-user-table) - (maphash (lambda (folded-nick user) - (when (< (irc-user-part-time user) - (- (float-time) - (* 60 60))) - (remhash folded-nick recent-user-table))) - recent-user-table)))) - -(defun irc-channel-rename-user (channel oldnick newnick) - "Update CHANNEL so that the user with nick OLDNICK now has nick NEWNICK." - (let ((user-table (irc-channel-users channel)) - (user (irc-channel-user channel oldnick)) - (newnick-folded (irc-isupport--case-fold - (irc-channel-connection channel) - newnick)) - (recent-user (irc-channel-recent-user channel newnick))) - (when user - (when (and recent-user - (equal (irc-user-userhost recent-user) - (irc-user-userhost user))) - (setf (irc-user-last-activity-time user) - (irc-user-last-activity-time recent-user))) - (remhash (irc-user-folded-nick user) user-table) - (setf (irc-user-nick user) newnick) - (setf (irc-user-folded-nick user) newnick-folded) - (puthash (irc-user-folded-nick user) user user-table)))) - -(defun irc-handle-state-tracking--rpl-welcome (conn _event _sender target - &rest ignored) - (irc-connection-put conn :current-nick target)) - -(defun irc-handle-state-tracking--JOIN (conn _event sender target - &optional _account _realname) - (let ((nick (irc-userstring-nick sender))) - (cond - ((irc-current-nick-p conn nick) - (irc-connection-add-channel conn target)) - (t - (let ((channel (irc-connection-channel conn target))) - (when channel - (let ((user (irc-channel-add-user channel sender))) - (when user - (setf (irc-user-join-time user) (float-time)))))))))) - -(defun irc-handle-state-tracking--PART (conn _event sender target - &optional _reason) - (let ((nick (irc-userstring-nick sender))) - (cond - ((irc-current-nick-p conn nick) - (irc-connection-remove-channel conn target)) - (t - (let ((channel (irc-connection-channel conn target))) - (when channel - (irc-channel-remove-user channel nick))))))) - -(defun irc-handle-state-tracking--KICK (conn _event _sender target nick - &optional _reason) - (cond - ((irc-current-nick-p conn nick) - (irc-connection-remove-channel conn target)) - (t - (let ((channel (irc-connection-channel conn target))) - (when channel - (irc-channel-remove-user channel nick)))))) - -(defun irc-handle-state-tracking--QUIT (conn _event sender - &optional reason) - (let ((nick (irc-userstring-nick sender))) - (if (irc-current-nick-p conn nick) - (dolist (channel (irc-connection-channel-list conn)) - (irc-connection-remove-channel conn - (irc-channel-folded-name channel))) - (dolist (channel (irc-connection-channel-list conn)) - (when (irc-channel-user channel nick) - (irc-event-emit conn "channel.quit" - sender - (irc-channel-name channel) - reason)) - (irc-channel-remove-user channel nick))))) - -(defun irc-handle-state-tracking--NICK (conn _event sender new-nick) - ;; Update channels - (let ((nick (irc-userstring-nick sender))) - (dolist (channel (irc-connection-channel-list conn)) - (irc-channel-rename-user channel nick new-nick))) - ;; Update our own nick - (when (irc-current-nick-p conn (irc-userstring-nick sender)) - (irc-connection-put conn :current-nick new-nick))) - -(defun irc-handle-state-tracking--PRIVMSG (conn _event sender target _message) - (let ((channel (irc-connection-channel conn target)) - (nick (irc-userstring-nick sender))) - (when channel - (let ((user (irc-channel-user channel nick))) - (when user - (setf (irc-user-last-activity-time user) (float-time))))))) - -(defun irc-handle-state-tracking--rpl-namreply - (conn _event _sender _current-nick _channel-type channel-name nicks) - (let ((channel (irc-connection-channel conn channel-name))) - (when channel - (setf (irc-channel-receiving-names channel) - (append (irc-channel-receiving-names channel) - (mapcar (lambda (nick) - (irc-nick-without-prefix - conn - (string-trim nick))) - (split-string nicks))))))) - -(defun irc-handle-state-tracking--rpl-endofnames - (conn _event _sender _current-nick channel-name _description) - (let ((channel (irc-connection-channel conn channel-name))) - (when channel - (irc-channel--synchronize-nicks channel - (irc-channel-receiving-names channel)) - (setf (irc-channel-receiving-names channel) nil)))) - -(defun irc-channel--synchronize-nicks (channel nicks) - "Update the user list of CHANNEL to match NICKS." - (let ((have (irc-channel-users channel)) - (want (make-hash-table :test 'equal))) - (dolist (nick nicks) - (puthash (irc-isupport--case-fold (irc-channel-connection channel) - nick) - nick - want)) - (maphash (lambda (nick-folded user) - (when (not (gethash nick-folded want)) - (irc-channel-remove-user channel - (irc-user-nick user)))) - have) - (maphash (lambda (_nick-folded nick) - (irc-channel-add-user channel nick)) - want))) - -(defun irc-handle-state-tracking--TOPIC (conn _event _sender channel new-topic) - (let ((channel (irc-connection-channel conn channel))) - (when channel - (setf (irc-channel-last-topic channel) - (irc-channel-topic channel)) - (setf (irc-channel-topic channel) new-topic)))) - -(defun irc-handle-state-tracking--rpl-notopic (conn _event _sender - _current-nick channel - _no-topic-desc) - (let ((channel (irc-connection-channel conn channel))) - (when channel - (setf (irc-channel-topic channel) nil)))) - -(defun irc-handle-state-tracking--rpl-topic (conn _event _sender _current-nick - channel topic) - (let ((channel (irc-connection-channel conn channel))) - (when channel - (setf (irc-channel-topic channel) topic)))) - -;;;;;;;;;;;;;;,;;;;;; -;;; Handler: NickServ - -(defun irc-handle-nickserv (table) - "Add command handlers to TABLE to deal with NickServ. - -Connection options used: - -:nickserv-nick -- The nick to register as - -:nickserv-password -- The password for nickserv; can be a function and - is then called with the IRC connection as its sole argument - -:nickserv-mask -- A regular expression matching the correct NickServ's - nick!user@host string to avoid fakes - -:nickserv-identify-challenge -- A regular expression matching the - challenge sent by NickServ to request identification - -:nickserv-identify-command -- The raw IRC command to send to identify; - expands {nick} and {password} when present - -:nickserv-identify-confirmation -- A regular expression matching the - confirmation message from NickServ after successful identification - -:nickserv-ghost-command -- The raw IRC comment to ghost your - original nick; expands {nick} and {password}. Set this to nil - to disable ghosting and nick regaining. - -:nickserv-ghost-confirmation -- A regular expression matching the - confirmation message that the nick was ghosted - -Events emitted: - -\"nickserv.identified\" -- We have successfully identified with nickserv. - -\"nickserv.ghosted\" -- We have ghosted a nick." - (irc-handler-add table "irc.registered" #'irc-handle-nickserv--registered) - (irc-handler-add table "NOTICE" #'irc-handle-nickserv--NOTICE) - (irc-handler-add table "PRIVMSG" #'irc-handle-nickserv--NOTICE) - (irc-handler-add table "NICK" #'irc-handle-nickserv--NICK)) - -(defun irc-handle-nickserv--password (conn) - (let ((password (irc-connection-get conn :nickserv-password))) - (if (functionp password) - (funcall password conn) - password))) - -(defun irc-handle-nickserv--registered (conn _event current-nick) - (let ((ghost-command (irc-connection-get conn :nickserv-ghost-command)) - (wanted-nick (irc-connection-get conn :nickserv-nick)) - (password (irc-handle-nickserv--password conn))) - (when (and ghost-command - wanted-nick - password - (not (irc-string-equal-p conn current-nick wanted-nick))) - (irc-send-raw conn - (irc-format ghost-command - 'nick wanted-nick - 'password password))))) - -(defun irc-handle-nickserv--NOTICE (conn _event sender _target message) - (let ((nickserv-mask (irc-connection-get conn :nickserv-mask)) - identify-challenge identify-command identify-confirmation - ghost-confirmation - nickserv-nick nickserv-password) - (when (and nickserv-mask (string-match nickserv-mask sender)) - (setq identify-challenge - (irc-connection-get conn :nickserv-identify-challenge)) - (setq identify-command - (irc-connection-get conn :nickserv-identify-command)) - (setq identify-confirmation - (irc-connection-get conn :nickserv-identify-confirmation)) - (setq ghost-confirmation - (irc-connection-get conn :nickserv-ghost-confirmation)) - (setq nickserv-nick (irc-connection-get conn :nickserv-nick)) - (setq nickserv-password (irc-handle-nickserv--password conn)) - (cond - ;; Identify - ((and identify-challenge - identify-command - nickserv-nick - nickserv-password - (string-match identify-challenge message)) - (irc-send-raw conn - (irc-format identify-command - 'nick nickserv-nick - 'password nickserv-password))) - ;; Identification confirmed - ((and identify-confirmation - (string-match identify-confirmation message)) - (irc-event-emit conn "nickserv.identified")) - ;; Ghosting confirmed - ((and ghost-confirmation - (string-match ghost-confirmation message)) - (irc-event-emit conn "nickserv.ghosted") - (irc-connection-put conn :nickserv-regaining-nick t) - (when nickserv-nick - (irc-send-NICK conn nickserv-nick))))))) - -(defun irc-handle-nickserv--NICK (conn _event _sender new-nick) - (when (and (irc-connection-get conn :nickserv-regaining-nick) - (irc-string-equal-p conn new-nick - (irc-connection-get conn :nickserv-nick))) - (irc-connection-put conn :nickserv-regaining-nick nil) - (irc-event-emit conn "nickserv.regained"))) - -(defun irc-format (format &rest args) - "Return a formatted version of FORMAT, using substitutions from ARGS. - -The substitutions are identified by braces ('{' and '}')." - (with-temp-buffer - (insert format) - (goto-char (point-min)) - (while (re-search-forward "{\\([^}]*\\)}" nil t) - (replace-match (format "%s" (plist-get args (intern (match-string 1)))) - t t)) - (buffer-string))) - -;;;;;;;;;;;;;;;;;;;;;; -;;; Handler: Auto-Join - -(defun irc-handle-auto-join (table) - "Add command handlers to TABLE to deal with NickServ. - -Connection options used: - -:auto-join-after-registration -- List of channels to join - immediately after registration with the server - -:auto-join-after-host-hiding -- List of channels to join - after our host was hidden - -:auto-join-after-nick-acquisition -- List of channels to join - after we gained our desired nick - -:auto-join-after-nickserv-identification -- List of channels - to join after we identified successfully with NickServ" - (irc-handler-add table "irc.registered" #'irc-handle-auto-join--registered) - (irc-handler-add table "396" ;; RPL_HOSTHIDDEN - #'irc-handle-auto-join--rpl-hosthidden) - (irc-handler-add table "nickserv.regained" - #'irc-handle-auto-join--nickserv-regained) - (irc-handler-add table "nickserv.identified" - #'irc-handle-auto-join--nickserv-identified) - (irc-handler-add table "sasl.login" - #'irc-handle-auto-join--sasl-login)) - -(defun irc-handle-auto-join--registered (conn _event _current-nick) - (dolist (channel (irc-connection-get conn :auto-join-after-registration)) - (irc-send-JOIN conn channel))) - -(defun irc-handle-auto-join--rpl-hosthidden (conn _event _sender _target _host - _description) - (dolist (channel (irc-connection-get conn :auto-join-after-host-hiding)) - (irc-send-JOIN conn channel))) - -(defun irc-handle-auto-join--nickserv-regained (conn _event) - (dolist (channel (irc-connection-get - conn :auto-join-after-nick-acquisition)) - (irc-send-JOIN conn channel))) - -(defun irc-handle-auto-join--nickserv-identified (conn event) - (dolist (channel (irc-connection-get - conn :auto-join-after-nickserv-identification)) - (irc-send-JOIN conn channel)) - (if (irc-string-equal-p conn - (irc-connection-get conn :nick) - (irc-connection-get conn :nickserv-nick)) - (irc-handle-auto-join--nickserv-regained conn event))) - -(defun irc-handle-auto-join--sasl-login (conn _event &rest ignored) - (dolist (channel (irc-connection-get - conn :auto-join-after-sasl-login)) - (irc-send-JOIN conn channel))) - -(provide 'irc) -;;; irc.el ends here |