Support Freenode IRC

Previously my ERC setup just supported Google's internal IRC. Now I have
Freenode for things like #nixos, #emacs.

This complicated my KBDs for cycling through IRC channels since certain channels
only exist on certain servers. To remedy this, I introduced a temporary solution
that looks up the server given a particular channel. This isn't ideal, but it
works for now.
This commit is contained in:
William Carroll 2019-12-24 13:38:06 +00:00
parent 50f99976e0
commit f182410fd2
2 changed files with 113 additions and 17 deletions

View file

@ -23,6 +23,7 @@
(require 'dotfiles) (require 'dotfiles)
(require 'bookmark) (require 'bookmark)
(require 'keyboard) (require 'keyboard)
(require 'irc)
(require 'email) (require 'email)
(require 'wpc-keybindings) (require 'wpc-keybindings)

View file

@ -13,24 +13,70 @@
(require 'erc) (require 'erc)
(require 'cycle) (require 'cycle)
(require 'string) (require 'string)
(require 'prelude)
(require 'alist)
(require 'set)
(require 'maybe)
(require 'macros)
(require 'password-store)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Configuration ;; Configuration
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defconst irc/enable-tests? t
"When t, run the tests defined herein.")
(setq erc-rename-buffers t) (setq erc-rename-buffers t)
(defvar irc/channels-cycle ;; TODO: Find a way to avoid putting "freenode" and "#freenode" as channels
(cycle/from-list ;; here. I'm doing it because when erc first connects, it's `(buffer-name)' is
'("#omg" "#london" "#panic" "#prod-team")) ;; "freenode", so when `irc/next-channel' is called, it 404s on the
"List of channels through which I can cycle.") ;; `cycle/contains?' call in `irc/channel->cycle" unless "freenode" is there. To
;; make matters even uglier, when `erc-join-channel' is called with "freenode"
;; as the value, it connects to the "#freenode" channel, so unless "#freenode"
;; exists in this cycle also, `irc/next-channel' breaks again. This doesn't
;; pass my smell test.
(defconst irc/server->channels
`(("irc.freenode.net" . ,(cycle/new "freenode" "#freenode" "#nixos" "#emacs" "#pass"))
("irc.corp.google.com" . ,(cycle/new "#omg" "#london" "#panic" "#prod-team")))
"Mapping of IRC servers to a cycle of my preferred channels.")
;; TODO: Assert that no two servers have a channel with the same name. We need
;; this because that's the assumption that underpins the `irc/channel->server'
;; function. This will probably be an O(n^2) operation.
(prelude/assert
(set/distinct? (set/from-list
(cycle/to-list
(alist/get "irc.freenode.net"
irc/server->channels)))
(set/from-list
(cycle/to-list
(alist/get "irc.corp.google.com"
irc/server->channels)))))
(defun irc/channel->server (server->channels channel)
"Resolve an IRC server from a given CHANNEL."
(let ((result (alist/find (lambda (k v) (cycle/contains? channel v))
server->channels)))
(prelude/assert (maybe/some? result))
result))
(defun irc/channel->cycle (server->channels channel)
"Resolve an IRC's channels cycle from a given CHANNEL."
(alist/get (irc/channel->server server->channels channel)
server->channels))
;; Setting `erc-join-buffer' to 'bury prevents erc from stealing focus of the ;; Setting `erc-join-buffer' to 'bury prevents erc from stealing focus of the
;; current buffer when it connects to IRC servers. ;; current buffer when it connects to IRC servers.
(setq erc-join-buffer 'bury) (setq erc-join-buffer 'bury)
;; TODO: Here is another horrible hack that should be revisted.
(setq erc-autojoin-channels-alist (setq erc-autojoin-channels-alist
`(("corp.google.com" . ,(cycle/to-list irc/channels-cycle)))) (->> irc/server->channels
(alist/map-values #'cycle/to-list)
(alist/map-keys (>> (s-chop-prefix "irc.")
(s-chop-suffix ".net")))))
(defcustom irc/install-kbds? t (defcustom irc/install-kbds? t
"When t, install the keybindings defined herein.") "When t, install the keybindings defined herein.")
@ -43,6 +89,34 @@
"Print message X in a structured way." "Print message X in a structured way."
(message (string/format "[irc.el] %s" x))) (message (string/format "[irc.el] %s" x)))
;; TODO: Integrate Google setup with Freenode setup.
;; TODO: Support function or KBD for switching to an ERC buffer.
(defun irc/kill-all-erc-processes ()
"Kills all ERC buffers and processes."
(interactive)
(->> (erc-buffer-list)
(-map #'kill-buffer)))
(defun irc/switch-to-erc-buffer ()
"Switch to an ERC buffer."
(interactive)
(let ((buffers (erc-buffer-list)))
(if (list/empty? buffers)
(error "[irc.el] No ERC buffers available")
(switch-to-buffer (list/head (erc-buffer-list))))))
(defun irc/connect-to-freenode ()
"Connect to Freenode IRC."
(interactive)
(erc-ssl :server "irc.freenode.net"
:port 6697
:nick "wpcarro"
:password (password-store-get "programming/irc/freenode")
:full-name "William Carroll"))
;; TODO: Handle failed connections.
(defun irc/connect-to-google () (defun irc/connect-to-google ()
"Connect to Google's Corp IRC using ERC." "Connect to Google's Corp IRC using ERC."
(interactive) (interactive)
@ -51,23 +125,32 @@
:nick "wpcarro" :nick "wpcarro"
:full-name "William Carroll")) :full-name "William Carroll"))
;; TODO: Prefer defining these with a less homespun solution. There is a
;; function call `erc-buffer-filter' that would be more appropriate for the
;; implementation of `irc/next-channel' and `irc/prev-channel'.
(defun irc/next-channel () (defun irc/next-channel ()
"Join the next channel in `irc/channels-cycle'." "Join the next channel for the active server."
(interactive) (interactive)
(erc-join-channel (with-current-buffer (current-buffer)
(cycle/next irc/channels-cycle)) (let ((cycle (irc/channel->cycle irc/server->channels (buffer-name))))
(irc/message (erc-join-channel
(string/format "Current IRC channel: %s" (cycle/next cycle))
(cycle/current irc/channels-cycle)))) (irc/message
(string/format "Current IRC channel: %s" (cycle/current cycle))))))
(defun irc/prev-channel () (defun irc/prev-channel ()
"Join the previous channel in `irc/channels-cycle'." "Join the previous channel for the active server."
(interactive) (interactive)
(erc-join-channel (with-current-buffer (current-buffer)
(cycle/prev irc/channels-cycle)) (let ((cycle (irc/channel->cycle irc/server->channels (buffer-name))))
(irc/message (erc-join-channel
(string/format "Current IRC channel: %s" (cycle/prev cycle))
(cycle/current irc/channels-cycle)))) (irc/message
(string/format "Current IRC channel: %s" (cycle/current cycle))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Keybindings
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(when irc/install-kbds? (when irc/install-kbds?
(general-define-key (general-define-key
@ -75,5 +158,17 @@
"<C-tab>" #'irc/next-channel "<C-tab>" #'irc/next-channel
"<C-S-iso-lefttab>" #'irc/prev-channel)) "<C-S-iso-lefttab>" #'irc/prev-channel))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Tests
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(when irc/enable-tests?
(prelude/assert
(equal
(irc/channel->server `(("irc.dairy.com" . ,(cycle/new "#cheese" "#milk"))
("irc.color.com" . ,(cycle/new "#red" "#blue")))
"#cheese")
"irc.dairy.com")))
(provide 'irc) (provide 'irc)
;;; irc.el ends here ;;; irc.el ends here