;;; gnus-conf.el --- My Gnus configurations -*- lexical-binding: t; -*- (require 'gnus) (require 'epa) (epa-file-enable) (setq gnus-select-method '(nnnil "")) (setq gnus-secondary-select-methods '(;; (nntp "news.gmane.io") (nnmaildir "gmail" (directory "~/.nnmaildir/gmail")) (nnmaildir "durand" (directory "~/.nnmaildir/mymail")) ;; (nnmaildir "sent" (directory "~/.nnmaildir")) ;; (nnimap "LocalMail" ;; (nnimap-address "localhost") ;; (nnimap-stream network) ;; (nnimap-server-port 143)) )) ;;; Prefer TEXT than HTML mails (with-eval-after-load "mm-decode" (add-to-list 'mm-discouraged-alternatives "text/html") (add-to-list 'mm-discouraged-alternatives "text/richtext")) ;;; display using display-buffer (defun durand-display-gnus (&rest _args) "Display the group buffer using display-buffer." (cond ((null (get-buffer "*Group*")) (user-error "No group buffer!")) (t (quit-window) (display-buffer (get-buffer "*Group*"))))) (advice-add #'gnus :after #'durand-display-gnus) (defun durand-gnus-kill-tab (&rest _args) "Kill the tab when exiting Gnus." (cond ((durand-member "email" (mapcar (lambda (tab) (cdr (assq 'name tab))) (funcall tab-bar-tabs-function)) #'string=) (tab-bar-close-tab-by-name "email")))) (defun durand-gnus-quit () "Quit and kill tab at the same time." (interactive) (bury-buffer) (durand-gnus-kill-tab)) (add-hook 'gnus-exit-gnus-hook #'durand-gnus-kill-tab) ;;; repunctuate sentences (add-hook 'gnus-part-display-hook 'gnus-treat-repunctuate) ;;;###autoload (defun gnus-treat-repunctuate () "Do a quick repunctuation." (interactive) (save-excursion (goto-char (point-min)) (repunctuate-sentences t))) ;;; To be able to see the encrypted mails sent by me. (setq mml-secure-openpgp-encrypt-to-self (list "mmemmew@gmail.com" "durand@jsdurand.xyz")) ;;; mail aliases (setq message-mail-alias-type 'abbrev) ;;; Decryptions and signatures (setq gnus-buttonized-mime-types '("multipart/signed" "multipart/alternative")) (setq mm-decrypt-option 'known) (setq mm-verify-option 'known) ;;; Update by mu4e (declare-function #'mu4e-update-mail-and-index "mu4e" (RUN-IN-BACKGROUND)) (define-key gnus-group-mode-map (vector 117) #'mu4e-update-mail-and-index) (define-key global-map (vector ?\C-c ?g) 'gnus) ;;; user settings (setq gnus-ignored-from-addresses (list "mmemmew@gmail.com" "durand@jsdurand.xyz")) (setq send-mail-function #'smtpmail-send-it) (setq smtpmail-smtp-user "durand@jsdurand.xyz") (setq gnus-user-agent '(emacs gnus config)) (setq mail-user-agent 'gnus-user-agent) (setq read-mail-command 'gnus) (set 'gnus-novice-user nil) (setq nnmail-expiry-wait 'immediate) ;;; parameters (setq gnus-parameters `((".*" (posting-style (gcc "nnmaildir+gmail:Sent"))))) (require 'gnus-msg) ;;; posting style (setq gnus-posting-styles '((".*" (signature "李俊緯") (address "mmemmew@gmail.com") (From (format "Durand <%s>" user-mail-address))) ((header "from" "protesilaos") (signature "Durand")) ((header "from" "tan\\|mlh\\|hsialc\\|tingyu") (name "李俊緯") (From (format "李俊緯 <%s>" user-mail-address)) (body "老師好:\n") (signature "生 俊緯")))) (setq gnus-gcc-mark-as-read t) ;;; Multiple SMTP servers ;;;; Set the correct server on sending mails (defvar durand-smtp-servers nil "The list of SMTP servers that I use. Each server is a list of the form: (SENDER SERVER PORT USERNAME) SENDER is a string to match the \"FROM\" field of EMails. SERVER is the server to use. PORT is the port to use. USERNAME is used to look up passwords in ~/.authinfo.gpg.") (setq durand-smtp-servers (list (list "durand@jsdurand.xyz" "mail.jsdurand.xyz" 587 "durand@jsdurand.xyz") (list "mmemmew@gmail.com" "smtp.gmail.com" 587 "mmemmew@gmail.com"))) (defun durand-set-smtp-server-message-send-and-exit () "Set SMTP server according to `durand-smtp-servers' and send mail." (interactive) ;; We always determine this automatically. (message-remove-header "X-Message-SMTP-Method") (let* ((sender (message-fetch-field "From")) (server-form (alist-get sender durand-smtp-servers nil nil #'string-match-p))) (cond ((and (null server-form) (y-or-n-p "Do you want to change the default SMTP server?")) (setq server-form (alist-get (completing-read "Choose a server: " durand-smtp-servers nil t) durand-smtp-servers nil nil #'string=)))) ;; if there is a server to use, we change to it (cond (server-form (message-add-header (format "X-Message-SMTP-Method: smtp %s %d %s" (car server-form) (cadr server-form) (caddr server-form))))) ;; send the message if possible (let ((xmess (message-fetch-field "X-Message-SMTP-Method"))) (cond (xmess (message (format "Sending message using '%s' with config '%s'" sender xmess)) (message-send-and-exit)) (user-error (concat "Could not find SMTP Server for this Sender address: %s." "You might want to correct it or add it to " "the list 'durand-smtp-servers'.") sender))))) (define-key message-mode-map (vector 3 3) #'durand-set-smtp-server-message-send-and-exit) ;;;; Choose the correct identity when entering message buffers (defvar durand-identities (list (list "mmew" "Durand " "nnmaildir+gmail:Sent") (list "durand" "Durand " "nnmaildir+durand:sent" "Durand")) "The list of identities to choose. Each element is of the form NICK ADDR GCC [SIG] NICK: The name to type to choose this identity. ADDR: The string that appears as the FROM field of the message. GCC: The string that appears as the GCC field of the message. SIG: Optionally include a signature.") (defun durand-choose-identity (&optional arg) "Choose an identity when entering `message-mode'. If ARG is non-nil, ask which identity to use. Otherwise use the identity named durand." (interactive "P") (let* (choice (identity-to-use (cond (arg (setq choice (completing-read "Choose an identity: " durand-identities nil t)) (alist-get choice durand-identities nil nil #'string=)) ((alist-get (setq choice "durand") durand-identities nil nil #'string=)) ((user-error "No identity crisis!")))) (from (car identity-to-use)) (gcc (cadr identity-to-use)) (sig (caddr identity-to-use))) (message-remove-header "From") (message-remove-header "Gcc") (message-add-header (format "From: %s" from)) (message-add-header (format "Gcc: %s" gcc)) ;; deal with signature (cond (sig (save-excursion (goto-char (point-max)) (cond ((re-search-backward "^-- $") ;; found a pre-existing signature; let me update this. (forward-line 1) (delete-region (point) (point-max)))) (insert sig)))) (message "Chose %s as the identity now!" choice))) (define-key message-mode-map (vector 3 ?i) #'durand-choose-identity) ;;; Call me an expert. (setq gnus-expert-user t) (setq gnus-agent t) (setq gnus-check-new-newsgroups 'ask-server) (setq gnus-read-active-file 'some) (setq gnus-use-dribble-file t) (setq gnus-always-read-dribble-file nil) ;;; treatings (setq gnus-treat-display-smileys nil) (setq gnus-treat-emphasize t) (setq gnus-treat-fill-article nil) (setq gnus-summary-goto-unread 'never) (add-hook 'gnus-group-mode-hook 'gnus-topic-mode) ;;; group line format (setq gnus-group-line-format "%M%S%p%P%5y:%B%(%g%)\n") ;; (setq gnus-topic-alist ;; '(("devel" "gmane.emacs.devel") ;; ("orgmode" "gmane.emacs.orgmode") ;; ("bug" "gmane.emacs.bugs") ;; ("emacs") ;; ("Gnus" "nndraft:drafts"))) ;;; thread sorting (setq gnus-thread-sort-functions '(gnus-thread-sort-by-most-recent-date gnus-thread-sort-by-most-recent-number)) (setq gnus-subthread-sort-functions 'gnus-thread-sort-by-date) (setq gnus-thread-hide-subtree nil) ;;; activate level (setq gnus-activate-level 4) (setq gnus-ignored-from-addresses "mmemmew\\.com") ;;; move between topics (define-key gnus-group-mode-map [?\M-n] 'gnus-topic-goto-next-topic) (define-key gnus-group-mode-map [?\M-p] 'gnus-topic-goto-previous-topic) ;;; exiting (define-key gnus-group-mode-map (vector ?q) #'durand-gnus-quit) (define-key gnus-group-mode-map (vector ?x) #'gnus-group-exit) ;;; agent key bindings (require 'gnus-agent) (define-key gnus-agent-summary-mode-map (vector ?$) #'previous-line) (define-key gnus-agent-summary-mode-map (vector ?ù) #'next-line) (define-key gnus-agent-summary-mode-map (vector ?n) #'gnus-summary-next-article) (define-key gnus-agent-summary-mode-map (vector ?p) #'gnus-summary-prev-article) (define-key gnus-agent-summary-mode-map (vector ?N) #'gnus-summary-next-unread-article) (define-key gnus-agent-summary-mode-map (vector ?P) #'gnus-summary-prev-unread-article) (define-key gnus-agent-summary-mode-map (vector ?o) #'gnus-summary-save-article) (define-key gnus-agent-summary-mode-map (vector ?y) #'gnus-summary-scroll-down) (define-key gnus-agent-summary-mode-map (vector ?!) #'gnus-summary-mark-as-processable) (define-key gnus-agent-summary-mode-map (vector ?\M-n) #'gnus-summary-next-thread) (define-key gnus-agent-summary-mode-map (vector ?\M-p) #'gnus-summary-prev-thread) (define-key gnus-agent-summary-mode-map (vector ?\C-\M-n) #'gnus-summary-next-group) (define-key gnus-agent-summary-mode-map (vector ?\C-\M-p) #'gnus-summary-prev-group) (define-key gnus-agent-summary-mode-map (vector ?z ?t) #'recenter-to-top) (define-key gnus-agent-summary-mode-map (vector ?z ?b) #'recenter-to-bottom) (define-key gnus-agent-summary-mode-map (vector ?z ?z) #'recenter-to-middle) (define-key gnus-agent-group-mode-map (vector ?n) #'gnus-group-next-group) (define-key gnus-agent-group-mode-map (vector ?p) #'gnus-group-prev-group) (define-key gnus-agent-group-mode-map (vector ?N) #'gnus-group-next-unread-group) (define-key gnus-agent-group-mode-map (vector ?P) #'gnus-group-prev-unread-group) ;;; article (require 'gnus-art) (define-key gnus-article-mode-map [?z ?t] 'recenter-to-top) (define-key gnus-article-mode-map [?z ?b] 'recenter-to-bottom) (define-key gnus-article-mode-map [?z ?z] 'recenter-to-middle) (define-key gnus-article-mode-map [?o] 'gnus-mime-inline-part) (setq gnus-article-sort-functions '((not gnus-article-sort-by-number) (not gnus-article-sort-by-date))) (setq gnus-html-frame-width 80) (setq gnus-inhibit-images nil) (setq gnus-max-image-proportion 0.7) ;;; Summary (setq gnus-auto-select-first nil) (setq gnus-summary-ignore-duplicates t) (setq gnus-suppress-duplicates t) (setq gnus-summary-goto-unread 'never) (setq gnus-summary-to-prefix "To: ") (setq gnus-summary-line-format "%U%R%z %-16,16&user-date; %*%(%-30,30a %B%s%)\n") (setq gnus-summary-mode-line-format "%p") (setq gnus-summary-make-false-root 'adopt) (setq gnus-sum-thread-tree-false-root "─┬> ") (setq gnus-sum-thread-tree-indent " ") (setq gnus-sum-thread-tree-leaf-with-other "├─> ") (setq gnus-sum-thread-tree-root "") (setq gnus-sum-thread-tree-single-leaf "└─> ") (setq gnus-sum-thread-tree-vertical "│") (setq gnus-summary-thread-gathering-function 'gnus-gather-threads-by-subject) ;;; disable auto-save for newrc file (defun durand-disable-auto-save-for-newsrc-ad (&rest _args) "Disable auto-save for newsrc file." (cond ((get-buffer ".newsrc-dribble") (with-current-buffer ".newsrc-dribble" (setq-local buffer-auto-save-file-name nil))))) (advice-add #'gnus :after #'durand-disable-auto-save-for-newsrc-ad) ;;; Supercite ;; This is suggested by the manual (autoload 'sc-cite-original "supercite" nil t) ;; This is the only thing we need to enable using Supercite (add-hook 'mail-citation-hook #'sc-cite-original) ;; I can start the eelectirc reference mode later on, by the function ;; `sc-insert-reference' (bound to "C-c C-p w" by default). (setq sc-electric-references-p nil) ;; I find this style quite balanced: not too verbose but still ;; informative enough (setq sc-preferred-header-style 1) ;; I used to use the nested style, but now I think the non-nested ;; style is clearer and cleaner (setq sc-nested-citation-p nil) ;; Don't query me each time (setq sc-confirm-always-p nil) (provide 'gnus-conf) ;;; gnus-conf.el ends here