| 1 | ;;; -*-emacs-lisp-*- |
| 2 | ;;; |
| 3 | ;;; ERC configuration |
| 4 | |
| 5 | (setq erc-nick "mdw" |
| 6 | erc-user-full-name "Mark Wooding") |
| 7 | |
| 8 | (dolist (module '(replace truncate)) |
| 9 | (if (not (memq module erc-modules)) |
| 10 | (setq erc-modules (cons module erc-modules)))) |
| 11 | |
| 12 | (setq erc-fill-column 76 |
| 13 | erc-timestamp-right-column 68 |
| 14 | erc-fill-prefix " " |
| 15 | erc-max-buffer-size (* 60 3000)) |
| 16 | |
| 17 | ;; Filter out emoji, which cause severe display confusion. |
| 18 | (defun mdw-replace-wide-characters (string) |
| 19 | (with-output-to-string |
| 20 | (let ((i 0) |
| 21 | (state nil)) |
| 22 | (while (< i (length string)) |
| 23 | (let ((ch (aref string i))) |
| 24 | (cond ((and (= (char-width ch) 1) |
| 25 | (not (or (<= #x1f200 ch #x1ffff) |
| 26 | (= ch #x2693)))) |
| 27 | (when state (princ "*]") (setf state nil)) |
| 28 | (write-char ch)) |
| 29 | (t |
| 30 | (princ (if state "*" "[*")) |
| 31 | (princ (format "#x%x" ch)) |
| 32 | (setf state t)))) |
| 33 | (setq i (1+ i))) |
| 34 | (when state (princ "*]"))))) |
| 35 | |
| 36 | (setq erc-replace-alist |
| 37 | '(("[[:nonascii:]+]" . mdw-replace-wide-characters))) |
| 38 | |
| 39 | (setq erc-track-exclude-types '("NICK" "JOIN" "PART")) |
| 40 | |
| 41 | (setq erc-auto-query 'buffer) |
| 42 | |
| 43 | (defun mdw-erc-turn-off-truncate-lines () |
| 44 | (setq truncate-lines nil |
| 45 | truncate-partial-with-windows nil |
| 46 | word-wrap t |
| 47 | wrap-prefix (concat (propertize " " 'face 'erc-prompt-face) |
| 48 | " "))) |
| 49 | (add-hook 'erc-mode-hook 'mdw-erc-turn-off-truncate-lines) |
| 50 | |
| 51 | (setq erc-autojoin t |
| 52 | erc-autojoin-domain-only nil |
| 53 | erc-autojoin-channels-alist |
| 54 | '(("chiark.greenend.org.uk" "#chiark") |
| 55 | ("irc.distorted.org.uk" "#distorted" "#jukebox") |
| 56 | ("irc.hstg.corp.good.com" "#hstg"))) |
| 57 | (erc-autojoin-mode 1) |
| 58 | |
| 59 | (defvar mdw-erc-auto-greet-bots-alist nil |
| 60 | "*Alist of (SERVER-REGEXP BOT-NICK MESSAGE-FORM). |
| 61 | Evaluate MESSAGE-FORM and sent to BOT-NICK when connected to a server which |
| 62 | matches SERVER-REGEXP.") |
| 63 | |
| 64 | (defvar mdw-erc-ircop-alist nil |
| 65 | "*Alist of (SERVER-REGEXP ACCT PASSWD). |
| 66 | Login details for claiming server admin rights.") |
| 67 | |
| 68 | (defun mdw-remprop-nondestructive (indic plist) |
| 69 | "Return a plist like PLIST, only without the first entry for INDIC. |
| 70 | The PLIST is not itself modified." |
| 71 | (if (getf plist indic) |
| 72 | (let* ((head (cons nil nil)) |
| 73 | (tail head)) |
| 74 | (while (and plist (not (eq (car plist) indic))) |
| 75 | (let* ((i (pop plist)) (v (pop plist)) |
| 76 | (vv (cons v nil)) (ii (cons i vv))) |
| 77 | (rplacd tail ii) |
| 78 | (setq tail vv))) |
| 79 | (rplacd tail (cddr plist)) |
| 80 | (cdr head)) |
| 81 | plist)) |
| 82 | |
| 83 | (defun* mdw-cons-replace |
| 84 | (item list &rest keys &key (key '#'identity) &allow-other-keys) |
| 85 | "Return LIST, with ITEM at the start, replacing any existing matching item. |
| 86 | Specifically, any item in the list satisfying the test are removed |
| 87 | \(nondestructively), and then the new ITEM is added to the front." |
| 88 | (cons item (apply #'remove* (funcall key item) list :key key |
| 89 | (mdw-remprop-nondestructive :key keys)))) |
| 90 | |
| 91 | (defmacro* mdw-pushnew-replace (item place &rest keys) |
| 92 | "Add ITEM to the list PLACE, replacing any existing matching item. |
| 93 | Specifically, any item in the list satisfying the test are removed |
| 94 | \(nondestructively), and then the new ITEM is added to the front. |
| 95 | |
| 96 | Evaluation order for the keywords is a bit screwy: don't rely on it." |
| 97 | (cond ((fboundp 'cl-callf2) |
| 98 | `(cl-callf2 mdw-cons-replace ,item ,place ,@keys)) |
| 99 | ((fboundp 'cl-setf-do-modify) |
| 100 | ;; `cl-setf-do-modify' returns a list (LETS STORE FETCH). |
| 101 | (let ((setf-things (cl-setf-do-modify place (cons 'list keys)))) |
| 102 | `(let (,@(car setf-things)) |
| 103 | ,(cl-setf-do-store (cadr setf-things) |
| 104 | `(mdw-cons-replace ,item ,place |
| 105 | ,@keys))))) |
| 106 | (t (error "Don't know how to hack places on this Emacs.")))) |
| 107 | |
| 108 | (defun mdw-define-bot-greeting (server bot greeting) |
| 109 | "Define a new bot greeting." |
| 110 | (mdw-pushnew-replace (list server bot greeting) |
| 111 | mdw-erc-auto-greet-bots-alist |
| 112 | :test #'string= :key #'car)) |
| 113 | (defun mdw-add-ircop-credentials (server acct passwd) |
| 114 | "Define a new set of `ircop' credentials." |
| 115 | (mdw-pushnew-replace (list server acct passwd) |
| 116 | mdw-erc-ircop-alist |
| 117 | :test #'string= :key #'car)) |
| 118 | |
| 119 | (defun mdw-assoc-regexp (regexp alist) |
| 120 | "Return the association in ALIST whose car matches REGEXP." |
| 121 | (let ((answer nil)) |
| 122 | (dolist (l alist) |
| 123 | (when (string-match (car l) regexp) |
| 124 | (setq answer l))) |
| 125 | answer)) |
| 126 | |
| 127 | (defun mdw-erc-auto-greet-bots (server nick) |
| 128 | "Send greeting message to bots." |
| 129 | (let ((a (mdw-assoc-regexp server mdw-erc-auto-greet-bots-alist))) |
| 130 | (when a |
| 131 | (let ((bot (cadr a)) |
| 132 | (message (caddr a))) |
| 133 | (erc-server-send (concat "PRIVMSG " bot " :" message)))))) |
| 134 | (add-hook 'erc-after-connect 'mdw-erc-auto-greet-bots) |
| 135 | |
| 136 | (defun erc-cmd-GREET () |
| 137 | "Send greeting messages, according to `mdw-erc-auto-greet-bots-alist'." |
| 138 | (mdw-erc-auto-greet-bots erc-session-server (erc-current-nick))) |
| 139 | |
| 140 | (defun erc-cmd-IRCOP () |
| 141 | "Claim `ircop' privileges." |
| 142 | (let ((a (mdw-assoc-regexp erc-session-server mdw-erc-ircop-alist))) |
| 143 | (when a |
| 144 | (let ((acct (cadr a)) |
| 145 | (passwd (caddr a))) |
| 146 | (erc-server-send (concat "OPER " acct " " passwd)))))) |
| 147 | |
| 148 | (load "~/.erc-auth.el") |
| 149 | (load "~/.erc-local.el") |