1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
;;; irc.el -*- lexical-binding: t; -*-
;;;
;;; Author: Jakob L. Kreuze <zerodaysfordays@sdf.org>
;;; URL: https://git.sr.ht/~jakob/.emacs.d/
;;;
;;; Configuration of ERC, as well as additional custom defuns for making an
;;; enjoyable IRC experience within Emacs.
;;;
;; Functions for connecting to and authenticating with my IRC bouncer.
(defun read-bouncer-password ()
"Pull the password for my bouncer from authinfo"
(let ((auth (auth-source-search :host "jakob.space" :user "jakob" :port 6697)))
(unless auth
(error "No auth entry for bouncer."))
(let ((secretf (plist-get (car auth) :secret)))
(unless secretf
(error "No secret for bouncer's auth entry."))
(funcall secretf))))
(defvar *bouncer-port-alist* '(("freenode" . 6697)
("tildetown" . 6671)))
(defun connect-bouncer (server)
"Connect to `SERVER' on the bouncer and rename the buffer"
(let* ((port (cdr (assoc server *bouncer-port-alist*)))
(buf (erc-tls :server "jakob.space"
:port port
:password (read-bouncer-password))))
(erc-with-buffer (buf (rename-buffer server)))))
;; Interactive function for connecting to all of my bouncer's IRC servers.
(defvar irc-autoconnect-networks '("freenode" "tildetown")
"List of networks to automatically connect to when 'irc' is invoked.")
(defun irc ()
"Connect to every network in `irc-autoconnect-networks'."
(interactive)
(dolist (network irc-autoconnect-networks)
(connect-bouncer network)))
(defun irc-quit ()
"Disconnects from and kills all buffers associated with ERC."
(interactive)
(let ((server-buffers (cl-remove-if-not #'erc-server-buffer-p (erc-buffer-list))))
(dolist (buffer server-buffers)
(erc-with-buffer (buffer)
(erc-quit-server (funcall erc-quit-reason nil))))))
(use-package erc
:straight nil
;; Only load the modules I actually use. This list deviates from the default
;; by including truncate, but omitting autojoin.
:config
(setq erc-modules '(button completion fill
irccontrols list match
menu move-to-prompt netsplit
networks noncommands readonly
ring stamp track truncate))
(erc-update-modules)
;; Configure personal details.
:init
(setq erc-nick '("jakob" "tsarfox")
erc-user-full-name "Jakob <http://jakob.space>"
erc-quit-reason (lambda (reason)
(or reason (concat "Program received signal "
"SIGSEGV, Segmentation fault."))))
;; Display the current nick in the prompt.
:init
(setq erc-prompt (lambda () (format "%s>" (erc-current-nick))))
;; Center everything.
:init
(setq erc-fill-function 'erc-fill-static
erc-fill-column 100
erc-fill-static-center 22)
;; Enable MIRC and control sequences so I can partake in figlet spam,
;; objectively the best part of the IRC experience.
:init
(setq erc-interpret-controls-p t
erc-interpret-mirc-color t)
;; Enable timestamps, and show them to the left of the messages.
:init
(setq erc-hide-timestamps nil
erc-insert-timestamp-function 'erc-insert-timestamp-left)
;; Although 'log' is omitted from `erc-modules', I still explicitly disable
;; logging. I can just save the buffer if I want to save a conversation.
:init
(setq erc-enable-logging nil
erc-save-buffer-on-part nil)
;; Similarly, 'autowaway' is omitted from `erc-modules', but I'm disabling it
;; anyway.
:init
(setq erc-auto-set-away nil
erc-auto-discard-away nil)
;; Kill buffers when I disconnect.
:init
(setq erc-kill-buffer-on-part t
erc-kill-queries-on-quit t
erc-kill-server-buffer-on-quit t)
;; Don't get in my way by popping new windows when someone queries me, and
;; don't assault me with windows when I connect to my bouncer.
:init
(setq erc-auto-query 'bury
erc-join-buffer 'bury)
;; Show CTCP requests. Not for any reason in particular, I just like seeing
;; when people ping me.
:init
(setq erc-paranoid t)
;; Hide JOIN/PART/QUIT messages from idlers.
:init
(setq erc-lurker-hide-list '("JOIN" "PART" "QUIT")
erc-lurker-threshold-time 3600)
;; Configure `erc-track' to ignore the less interesting message types.
:init
(setq erc-track-exclude-server-buffer t
erc-track-exclude-types '("JOIN" "NICK" "PART" "QUIT" "MODE"
"324" "329" "332" "333" "353" "477"))
;; Enable channel tracking.
:config
(erc-track-minor-mode 1)
(erc-track-mode 1)
;; Move to the prompt as soon as I begin typing. I've configured the buffer
;; text preceding it to be read-only, anyway.
:config
(erc-move-to-prompt-enable)
;; Bind a key for resetting =erc-track=, since I tend to get absolutely
;; bombarded upon reconnecting.
:bind
(:map erc-mode-map
("C-c f" . (lambda ()
(interactive)
(setq erc-modified-channels-alist nil)
(erc-modified-channels-update))))
;; Let `erc-fill' handle filling.
:hook
(erc-mode . (lambda () (auto-fill-mode 0))))
;; Configure 'erc-hl-nicks' to show names using pretty colors.
(use-package erc-hl-nicks
:after erc
:config
(erc-hl-nicks-enable))
;; Display a bar delimiting read and unread messages. Adapted from [1].
;;
;; [1]: https://www.emacswiki.org/emacs/ErcBar
(defvar erc-bar-overlay nil
"Overlay used to set bar")
(defvar erc-bar-threshold 1
"The minimum number of messages required for the bar to be
displayed.")
(defun erc-bar-move-back (n)
"Moves back n message lines. Ignores wrapping, and server
messages."
(interactive "nHow many lines ? ")
(re-search-backward "^.*<.*>" nil t n))
(defun erc-bar-update-overlay ()
"Update the overlay for current buffer, based on the content of
erc-modified-channels-alist. Should be executed on window
change."
(interactive)
(let* ((info (assq (current-buffer) erc-modified-channels-alist))
(count (cadr info)))
(if (and info (> count erc-bar-threshold))
(save-excursion
(goto-char (point-max))
(when (erc-bar-move-back count)
(let ((inhibit-field-text-motion t))
(move-overlay erc-bar-overlay
(line-beginning-position)
(line-end-position)
(current-buffer)))))
(delete-overlay erc-bar-overlay))))
(with-eval-after-load 'erc
;; Run the hook before 'erc-modified-channels-update
(defadvice erc-track-mode (after erc-bar-setup-hook
(&rest args) activate)
(remove-hook 'window-configuration-change-hook 'erc-bar-update-overlay)
(add-hook 'window-configuration-change-hook 'erc-bar-update-overlay))
(add-hook 'erc-send-completed-hook (lambda (str)
(ignore str)
(erc-bar-update-overlay))))
;; Display the bar as a highlight on the most recently unread message.
(with-palette
(setq erc-bar-overlay (make-overlay 0 0))
(overlay-put erc-bar-overlay 'face `(:background ,black-2)))
|