diff options
| author | Craig Jennings <c@cjennings.net> | 2025-10-12 11:47:26 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-10-12 11:47:26 -0500 |
| commit | 092304d9e0ccc37cc0ddaa9b136457e56a1cac20 (patch) | |
| tree | ea81999b8442246c978b364dd90e8c752af50db5 /modules/erc-config.el | |
changing repositories
Diffstat (limited to 'modules/erc-config.el')
| -rw-r--r-- | modules/erc-config.el | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/modules/erc-config.el b/modules/erc-config.el new file mode 100644 index 00000000..03c89aca --- /dev/null +++ b/modules/erc-config.el @@ -0,0 +1,317 @@ +;;; erc-config --- Preferences for Emacs Relay Chat (IRC Client) -*- lexical-binding: t; coding: utf-8; -*- +;; author Craig Jennings <c@cjennings.net> + +;;; Commentary: + +;; Enhanced ERC configuration with multi-server support. +;; +;; Main keybindings: +;; - C-c e C : Select and connect to a specific server +;; - C-c e c : Join a channel on current server +;; - C-c e b : Switch between ERC buffers across all servers +;; - C-c C-q : Quit current channel +;; - C-c C-Q : Quit ERC altogether + +;;; Code: + +;; Keymap for ERC commands +(defvar cj/erc-command-map + (let ((map (make-sparse-keymap))) + (define-key map "C" 'cj/erc-connect-server-with-completion) ;; Connect to server (capital C) + (define-key map "c" 'cj/erc-join-channel-with-completion) ;; join channel (lowercase c) + (define-key map "b" 'cj/erc-switch-to-buffer-with-completion) ;; switch Buffer + (define-key map "l" 'cj/erc-connected-servers) ;; print connected servers in echo area + (define-key map "q" 'erc-part-from-channel) ;; quit channel + (define-key map "Q" 'erc-quit-server) ;; Quit ERC entirely + map) + "Keymap for ERC-related commands.") + +(global-set-key (kbd "C-c e") cj/erc-command-map) + +;; ------------------------------------ ERC ------------------------------------ +;; Server definitions and connection settings + +(defvar cj/erc-server-alist + '(("Libera.Chat" + :host "irc.libera.chat" + :port 6697 + :tls t + :channels ("#erc" "#emacs" "#emacs-social" "#systemcrafters")) + + ("IRCnet" + :host "open.ircnet.net" + :port 6697 + :tls t + :channels ("#english")) + + ("Snoonet" + :host "irc.snoonet.org" + :port 6697 + :tls t + :channels ("#talk")) + + ("IRCNow" + :host "irc.ircnow.org" + :port 6697 + :tls t + :channels ("#general" "#lounge"))) + "Alist of IRC servers and their connection details.") + +(defun cj/erc-connect-server (server-name) + "Connect to a server specified by SERVER-NAME from =cj/erc-server-alist=." + (let* ((server-info (assoc server-name cj/erc-server-alist)) + (host (plist-get (cdr server-info) :host)) + (port (plist-get (cdr server-info) :port)) + (tls (plist-get (cdr server-info) :tls))) + (if tls + (erc-tls :server host + :port port + :nick "craigjennings" + :full-name user-whole-name) + (erc :server host + :port port + :nick "craigjennings" + :full-name user-whole-name)))) + +(defun cj/erc-connect-server-with-completion () + "Connect to a server using completion for server selection." + (interactive) + (let ((server-name (completing-read "Connect to IRC server: " + (mapcar #'car cj/erc-server-alist)))) + (cj/erc-connect-server server-name))) + +(defun cj/erc-connected-servers () + "Return a list of currently connected servers and display them in echo area." + (interactive) + (let ((server-buffers '())) + (dolist (buf (erc-buffer-list)) + (with-current-buffer buf + (when (eq (buffer-local-value 'erc-server-process buf) erc-server-process) + (unless (member (buffer-name) server-buffers) + (push (buffer-name) server-buffers))))) + + ;; Display the server list when called interactively + (when (called-interactively-p 'any) + (if server-buffers + (message "Connected ERC servers: %s" + (mapconcat 'identity server-buffers ", ")) + (message "No active ERC server connections"))) + + server-buffers)) + +(defun cj/erc-switch-to-buffer-with-completion () + "Switch to an ERC buffer using completion." + (interactive) + (let* ((erc-buffers (mapcar 'buffer-name (erc-buffer-list))) + (selected (completing-read "Switch to buffer: " erc-buffers))) + (switch-to-buffer selected))) + +(defun cj/erc-server-buffer-active-p () + "Return t if the current buffer is an active ERC server buffer." + (and (derived-mode-p 'erc-mode) + (erc-server-process-alive) + (erc-server-buffer-p))) + +(defun cj/erc-join-channel-with-completion () + "Join a channel on the current server. + +If not in an active ERC server buffer, reconnect first." + (interactive) + (unless (cj/erc-server-buffer-active-p) + (if (erc-buffer-list) + ;; We have ERC buffers, but current one isn't active + (let ((server-buffers (cl-remove-if-not + (lambda (buf) + (with-current-buffer buf + (and (erc-server-buffer-p) + (erc-server-process-alive)))) + (erc-buffer-list)))) + (if server-buffers + ;; Found active server buffer, switch to it + (switch-to-buffer (car server-buffers)) + ;; No active server buffer, reconnect + (message "No active ERC connection. Reconnecting...") + (call-interactively 'cj/erc-connect-server-with-completion))) + ;; No ERC buffers at all, connect to a server + (message "No active ERC connection. Connecting to server first...") + (call-interactively 'cj/erc-connect-server-with-completion))) + + ;; At this point we should have an active connection + (if (cj/erc-server-buffer-active-p) + (let ((channel (read-string "Join channel: "))) + (when (string-prefix-p "#" channel) + (erc-join-channel channel))) + (message "Failed to establish an active ERC connection"))) + + +;; Main ERC configuration +(use-package erc + :defer 1 + :ensure nil ;; built-in + :commands (erc erc-tls) + :hook + (erc-mode . emojify-mode) + :custom + (erc-modules + '(autojoin + button + completion + fill + irccontrols + list + log + match + move-to-prompt + noncommands + notifications + readonly + services + stamp + track)) ;; Added track module + + (erc-autojoin-channels-alist + (mapcar (lambda (server) + (cons (car server) + (plist-get (cdr server) :channels))) + cj/erc-server-alist)) + + (erc-nick "craigjennings") + (erc-user-full-name user-whole-name) + (erc-use-auth-source-for-nickserv-password t) + (erc-kill-buffer-on-part t) + (erc-kill-queries-on-quit t) + (erc-kill-server-buffer-on-quit t) + (erc-fill-column 120) + (erc-fill-function 'erc-fill-static) + (erc-fill-static-center 20) + + :config + + + ;; use all text mode abbrevs in ercmode + (abbrev-table-put erc-mode-abbrev-table :parents (list text-mode-abbrev-table)) + + ;; create log directory if it doesn't exist + (setq erc-log-channels-directory (concat user-emacs-directory "erc/logs/")) + (if (not (file-exists-p erc-log-channels-directory)) + (mkdir erc-log-channels-directory t)) + + ;; Configure buffer naming to include server name + (setq erc-rename-buffers t) + (setq erc-unique-buffers t) + + ;; Custom buffer naming function + (defun cj/erc-generate-buffer-name (parms) + "Generate buffer name in the format SERVER-CHANNEL." + (let ((network (plist-get parms :server)) + (target (plist-get parms :target))) + (if target + (concat (or network "") "-" (or target "")) + (or network "")))) + + (setq erc-generate-buffer-name-function 'cj/erc-generate-buffer-name) + + ;; Configure erc-track (show channel activity in modeline) + (setq erc-track-exclude-types '("JOIN" "NICK" "PART" "QUIT" "MODE" + "324" "329" "332" "333" "353" "477") + erc-track-exclude-server-buffer t + erc-track-visibility 'selected-visible + erc-track-switch-direction 'importance + erc-track-showcount t)) + +;; -------------------------------- ERC Track --------------------------------- +;; Better tracking of activity across channels (already included in modules above) + +(use-package erc-track + :ensure nil ;; built-in + :after erc + :custom + (erc-track-position-in-mode-line 'before-modes) + (erc-track-shorten-function 'erc-track-shorten-names) + (erc-track-shorten-cutoff 8) + (erc-track-shorten-start 1) + (erc-track-priority-faces-only 'all) + (erc-track-faces-priority-list + '(erc-error-face + erc-current-nick-face + erc-keyword-face + erc-nick-msg-face + erc-direct-msg-face + erc-notice-face + erc-prompt-face))) + +;; ------------------------ ERC Desktop Notifications ------------------------ +;; Implementation for desktop notifications + +(defun cj/erc-notify-on-mention (match-type nick message) + "Display a notification when MATCH-TYPE is 'current-nick. + +NICK is the sender and MESSAGE is the message text." + (when (and (eq match-type 'current-nick) + (not (string= nick (erc-current-nick))) + (display-graphic-p)) + (let ((title (format "ERC: %s mentioned you" nick))) + ;; Use alert.el if available, otherwise fall back to notifications + (if (fboundp 'alert) + (alert message :title title :category 'erc) + (when (fboundp 'notifications-notify) + (notifications-notify + :title title + :body message + :app-name "Emacs ERC" + :sound-name 'message)))))) + +(add-hook 'erc-text-matched-hook 'cj/erc-notify-on-mention) + +;; ------------------------------ ERC Colorize ------------------------------- +;; Better color management with built-in functionality + +(defun cj/erc-colorize-setup () + "Setup ERC colorization for nicknames." + (make-local-variable 'erc-nick-color-alist) + (setq erc-nick-color-alist + (cl-loop for i from 0 to 15 + for color in '("blue" "green" "red" "brown" "purple" + "olive" "dark cyan" "light gray" "dark gray" + "light blue" "light green" "light red" + "light brown" "light purple" "yellow" "white") + collect (cons i color))) + (setq erc-nick-color-function 'erc-get-color-for-nick)) + +(add-hook 'erc-mode-hook 'cj/erc-colorize-setup) + +;; -------------------------------- ERC Image --------------------------------- +;; show inlined images (png/jpg/gif/svg) in erc buffers. + +(use-package erc-image + :defer 1 + :after erc + :config + (setq erc-image-inline-rescale 300) + (add-to-list 'erc-modules 'image) + (erc-update-modules)) + +;; -------------------------------- ERC Hl Nicks ------------------------------- +;; uniquely identify names in ERC + +(use-package erc-hl-nicks + :defer 1 + :after erc + :config + (add-to-list 'erc-modules 'hl-nicks) + (erc-update-modules)) + +;; ------------------------------ ERC Yank To Gist ----------------------------- +;; automatically create a Gist if pasting more than 5 lines +;; this module requires https://github.com/defunkt/gist +;; via ruby: 'gem install gist' via the aur: yay -S gist + +(use-package erc-yank + :defer 1 + :after erc + :bind + (:map erc-mode-map + ("C-y" . erc-yank))) + +(provide 'erc-config) +;;; erc-config.el ends here |
