diff options
Diffstat (limited to 'custom/jao-custom-gnus.el')
-rw-r--r-- | custom/jao-custom-gnus.el | 767 |
1 files changed, 767 insertions, 0 deletions
diff --git a/custom/jao-custom-gnus.el b/custom/jao-custom-gnus.el new file mode 100644 index 0000000..b7eb851 --- /dev/null +++ b/custom/jao-custom-gnus.el @@ -0,0 +1,767 @@ +;; gnus configuration -*- lexical-binding: t -*- + +;;; features +(defvar jao-gnus-use-local-imap nil) +(defvar jao-gnus-use-leafnode nil) +(defvar jao-gnus-use-gandi-imap nil) +(defvar jao-gnus-use-pm-imap nil) +(defvar jao-gnus-use-gmane nil) +(defvar jao-gnus-use-nnml nil) +(defvar jao-gnus-use-maildirs nil) +(defvar jao-notmuch-enabled nil) +;;; directories +(defun jao-gnus-dir (dir) + (expand-file-name dir gnus-home-directory)) + +(setq smtpmail-queue-dir (jao-gnus-dir "Mail/queued-mail/")) + +(setq mail-source-directory (jao-gnus-dir "Mail/") + message-directory (jao-gnus-dir "Mail/")) + +(setq gnus-default-directory (expand-file-name "~") + gnus-startup-file (jao-gnus-dir "newsrc") + gnus-agent-directory (jao-gnus-dir "News/agent") + gnus-home-score-file (jao-gnus-dir "scores") + gnus-article-save-directory (jao-gnus-dir "saved/") + nntp-authinfo-file (jao-gnus-dir "authinfo") + nnmail-message-id-cache-file (jao-gnus-dir "nnmail-cache") + nndraft-directory (jao-gnus-dir "drafts") + nnrss-directory (jao-gnus-dir "rss")) + +;;; looks +;;;; verbosity +(setq gnus-verbose 4) +;;;; geometry +(defvar jao-gnus-use-three-panes t) +(defvar jao-gnus-groups-width 50) +(defvar jao-gnus-wide-width 190) + +(setq gnus-use-trees nil + gnus-generate-tree-function 'gnus-generate-horizontal-tree + gnus-tree-minimize-window nil) + +(when jao-gnus-use-three-panes + + ;; (dolist (m '(calendar-mode org-agenda-mode gnus-group-mode)) + ;; (add-to-list 'display-buffer-alist `((major-mode . ,m) (dedicated t)))) + + (setq calendar-left-margin 6) + + (let ((side-bar '(vertical 1.0 + ("inbox.org" 0.4) + ("*Org Agenda*" 1.0) + ("*Calendar*" 8))) + (wide-len jao-gnus-wide-width) + (groups-len jao-gnus-groups-width) + (summary-len (- jao-gnus-wide-width jao-gnus-groups-width))) + (gnus-add-configuration + `(article + (horizontal 1.0 + (vertical ,groups-len (group 1.0)) + (vertical ,summary-len + (summary 0.25 point) + (article 1.0)) + ,side-bar))) + + (gnus-add-configuration + `(group (horizontal 1.0 (group ,wide-len point) ,side-bar))) + + (gnus-add-configuration + `(message (horizontal 1.0 (message ,wide-len point) ,side-bar))) + + (gnus-add-configuration + `(reply-yank (horizontal 1.0 (message ,wide-len point) ,side-bar))) + + (gnus-add-configuration + `(summary + (horizontal 1.0 + (vertical ,groups-len (group 1.0)) + (vertical ,summary-len (summary 1.0 point)) + ,side-bar))) + + (gnus-add-configuration + `(reply + (horizontal 1.0 + (message ,(- wide-len 100) point) + (article 100) + ,side-bar))))) + +;;;; no blue icon +(advice-add 'gnus-mode-line-buffer-identification :override #'identity) +(setq gnus-mode-line-image-cache nil) + +;;; search +(setq gnus-search-use-parsed-queries nil + gnus-search-notmuch-raw-queries-p nil + gnus-permanently-visible-groups "^nnselect:.*" + gnus-search-ignored-newsgroups "nndraft.*\\|nnselect.*") + +(with-eval-after-load "gnus-search" + (defclass gnus-search-recoll (gnus-search-indexed) + ((separator :type string :initform ".") + (program :initform "recoll") + (raw-queries-p :initform t))) + + (cl-defmethod gnus-search-indexed-extract ((_engine gnus-search-recoll)) + (prog1 (and (looking-at "^file://\\(.+\\)$") (list (match-string 1) 100)) + (forward-line 1))) + + (cl-defmethod gnus-search-transform-expression ((_engine gnus-search-recoll) + expr) + expr) + + (cl-defmethod gnus-search-indexed-search-command ((engine gnus-search-recoll) + (qstring string) + _query + &optional groups) + (let* ((subdir (slot-value engine 'remove-prefix)) + (sep (slot-value engine 'separator)) + (gdirs (mapcar (lambda (g) + (let ((g (gnus-group-short-name g))) + (replace-regexp-in-string "\\." sep g))) + (or groups + (and (not (string= "" subdir)) (list subdir))))) + (dirsq (and gdirs + (concat "(" + (mapconcat (lambda (d) (format "dir:%s" d)) + gdirs " OR ") + ")"))) + (q (concat "mime:message " dirsq " (" qstring ")"))) + ;; (message "query is: %s" q) + `("-b" "-t" "-q" ,q)))) + +;; (add-to-list 'gnus-parameters '("^nnselect:.*" (nnselect-rescan . t))) + +;;; news +(defvar jao-gnus-leafnode-spool "/var/spool/news/") +(setq gnus-select-method + (cond + (jao-gnus-use-leafnode + `(nntp "localhost" + (gnus-search-engine gnus-search-recoll + (remove-prefix ,jao-gnus-leafnode-spool) + (separator "/")))) + (jao-gnus-use-gmane '(nntp "news.gmane.io")) + (t '(nnnil "")))) + +(setq gnus-secondary-select-methods '()) + +(setq nnheader-read-timeout 0.02 + gnus-save-newsrc-file nil) ; .newsrc only needed by other newsreaders + +;; leafnode articles group parameters +(defvar jao-gnus-image-groups '("xkcd")) + +(defvar jao-gnus-leafnode-group-params + `((,(format "gwene\\..*%s.*" (regexp-opt jao-gnus-image-groups)) + (mm-html-inhibit-images nil) + (mm-html-blocked-images nil)) + ("\\(gmane\\|gwene\\)\\..*" + (jao-gnus--archiving-group "nnml:feeds.trove") + (posting-style (address "jao@gnu.org"))))) + +(when jao-gnus-use-leafnode + (dolist (p jao-gnus-leafnode-group-params) + (add-to-list 'gnus-parameters p t))) + +;;; mail +;;;; nnmail +(setq nnmail-treat-duplicates 'delete + nnmail-scan-directory-mail-source-once nil + nnmail-cache-accepted-message-ids t + nnmail-message-id-cache-length 100000 + nnmail-split-fancy-with-parent-ignore-groups nil + nnmail-use-long-file-names t + nnmail-crosspost t + nnmail-resplit-incoming t + nnmail-mail-splitting-decodes t + nnmail-split-methods 'nnmail-split-fancy) + +;;;; nnml +(setq gnus-message-archive-group nil + nnml-get-new-mail t + nnml-directory message-directory) + +(setq mail-sources + (let* ((pwd (auth-source-pick-first-password :host "proton-bridge")) + (mds (mapcar (lambda (f) + `(maildir :path ,(expand-file-name f "~/var/mail/"))) + '("local/" "feeds/"))) + (ims (mapcar (lambda (b) + `(imap :server "127.0.0.1" :port 1143 + :user "mail@jao.io" :password ,pwd + :stream starttls :predicate "1:*" + :fetchflag "\\Deleted \\Seen" + :mailbox ,(concat "Labels/#" b))) + '("inbox" "drivel" "hacking" "bills" + "bigml" "prog" "words")))) + (append mds ims))) + +(when jao-gnus-use-nnml + (add-to-list + 'gnus-secondary-select-methods + `(nnml "" (gnus-search-engine gnus-search-recoll + (remove-prefix ,(jao-gnus-dir "Mail/")))))) + +(defvar jao-gnus-nnml-group-params + `(("nnml:\\(local\\|trash\\|spam\\)" + (auto-expire . t) + (total-expire . t) + (expiry-wait . 1) + (expiry-target . delete)) + ("nnml:jao\\..*" + (posting-style ("Gcc" "nnml:jao.trove")) ;; ("Bcc" "proton@jao.io") + (jao-gnus--trash-group "nnml:trash") + (jao-gnus--spam-group "nnml:spam") + (jao-gnus--archiving-group "nnml:jao.trove") + (gcc-self . t)) + ("nnml:jao\\.hacking" + (posting-style ("Gcc" "nnml:jao.hacking") + (address "jao@gnu.org"))) ;; ("Bcc" "hacking@jao.io") + ("nnml:jao\\.drivel" + (auto-expire . t) + (total-expire . t) + (expiry-wait . 3) + (expiry-target . delete)) + ("nnml:bigml\\..*" + (gcc-self . nil) + (auto-expire . t) + (total-expire . t) + (expiry-wait . 3) + (expiry-target . delete) + (posting-style (address "jao@bigml.com")) + (jao-gnus--trash-group "nnml:trash") + (jao-gnus--spam-group "nnml:spam") + (jao-gnus--archiving-group "nnml:bigml.trove")) + ("nnml:bigml\\.inbox" + (gcc-self . t) + (auto-expire . t) + (total-expire . t) + (expiry-wait . 7) + (expiry-target . "nnml:bigml.trove")) + ("nnml:bigml\\.alba" + (gcc-self . t) + (auto-expire . nil) + (total-expire . nil) + (expiry-target . nil) + (expiry-wait . nil)) + ("nnml:bigml\\.trove" + (auto-expire . t) + (total-expire . t) + (expiry-target . delete) + (expiry-wait . 365)) + ("nnml:feeds\\.\\(.*\\)" + (posting-style ("Gcc" "nnml:feeds.trove") + (address "jao@gnu.org")) + (auto-expire . t) + (total-expire . t) + (expiry-wait . 7) + (expiry-target . delete) + (comment . "feeds.\\1") + (jao-gnus--archiving-group "nnml:feeds.trove")) + ("nnml:feeds\\.\\(news\\|emacs-\\(bugs\\|diffs|\\github\\)\\)$" + (expiry-wait . 2)) + ("nnml:feeds\\.trove" + (auto-expire . nil) + (total-expire . nil)) + ("nnml:feeds\\.fun" + (mm-html-inhibit-images nil) + (mm-html-blocked-images nil)))) + +(when jao-gnus-use-nnml + (dolist (p jao-gnus-nnml-group-params) + (add-to-list 'gnus-parameters p t))) + +;;;; imap +(setq nnimap-quirks nil) + +(when jao-gnus-use-local-imap + (add-to-list 'gnus-secondary-select-methods + `(nnimap "" (nnimap-address "localhost")))) + +(when jao-gnus-use-pm-imap + (add-to-list 'gnus-secondary-select-methods + '(nnimap "pm" + (nnimap-address "127.0.0.1") + (nnimap-stream network) + (nnimap-server-port 1143)))) + +(when jao-gnus-use-gandi-imap + (add-to-list 'gnus-secondary-select-methods + '(nnimap "gandi" (nnimap-address "mail.gandi.net")))) + +;;; groups +(setq gnus-group-line-format " %m%S%p%3y%P%* %~(pad-right 30)G %B\n" + ;; " %m%S%p%P:%~(pad-right 35)c %3y %B\n" + ;; " %m%S%p%3y%P%* %~(pad-right 30)C %B\n" + gnus-topic-line-format "%i[ %(%{%n%}%) -- %A ]%v\n" + gnus-group-uncollapsed-levels 2 + gnus-auto-select-subject 'unread + gnus-large-newsgroup 2000) + +(add-hook 'gnus-select-group-hook 'gnus-group-set-timestamp) +(add-hook 'gnus-group-mode-hook 'gnus-topic-mode) + +;;; summary +;;;; configuration +(setq gnus-summary-ignore-duplicates t + gnus-suppress-duplicates t + ;; gnus-summary-ignored-from-addresses jao-mails-regexp + gnus-process-mark-toggle t + gnus-auto-select-next 'almost-quietly) + +;;;; threading +(setq gnus-face-1 'jao-gnus-face-tree + gnus-show-threads t + gnus-thread-hide-subtree t + gnus-build-sparse-threads nil + gnus-refer-thread-use-search t + gnus-summary-make-false-root 'adopt + gnus-summary-gather-subject-limit nil ;; 120 + gnus-summary-thread-gathering-function #'gnus-gather-threads-by-subject + gnus-sort-gathered-threads-function 'gnus-thread-sort-by-number + gnus-thread-sort-functions '(gnus-thread-sort-by-number)) + +(defun jao-fix-protonmail-references (header) + (let ((references (mail-header-references header))) + (setf (mail-header-references header) + (mapconcat #'(lambda (x) + (if (string-match "protonmail.internalid" x) "" x)) + (gnus-split-references references) + " ")) + header)) + +(setq gnus-alter-header-function 'jao-fix-protonmail-references) + +;;;; search on enter nnselect +(defun jao-gnus--maybe-reselect (&rest _i) + (when (string-match-p "^nnselect" (or (gnus-group-name-at-point) "")) + (save-excursion (gnus-group-get-new-news-this-group)))) + +(advice-add 'gnus-group-select-group :before #'jao-gnus--maybe-reselect) + +;;;; summary line +(setq gnus-not-empty-thread-mark ?↓) ; ↓) ?· +(setq jao-gnus--summary-line-fmt + (concat "%%U %%*%%R %%uj " + "[ %%~(max-right 23)~(pad-right 23)uf " + " %%I%%~(pad-left 2)t ] %%s" + "%%-%s=" + "%%~(max-right 8)~(pad-left 8)&user-date;" + "\n")) + +(defun jao-gnus--set-summary-line (&optional w) + (let* ((d (if jao-gnus-use-three-panes (+ jao-gnus-groups-width 11) 12)) + (w (- (or w (window-width)) d))) + (setq gnus-summary-line-format (format jao-gnus--summary-line-fmt w)))) + +(add-hook 'gnus-select-group-hook 'jao-gnus--set-summary-line) +;; (jao-gnus--set-summary-line 187) + +(add-to-list 'nnmail-extra-headers 'Cc) +(add-to-list 'nnmail-extra-headers 'BCc) + +(use-package gnus-sum + :config + (add-to-list 'gnus-extra-headers 'Cc) + (add-to-list 'gnus-extra-headers 'BCc)) + + +(defun gnus-user-format-function-j (headers) + (let ((to (gnus-extra-header 'To headers))) + (if (string-match jao-mails-regexp to) + (if (string-match "," to) "¬" "»") ;; "~" "=") + (if (or (string-match jao-mails-regexp + (gnus-extra-header 'Cc headers)) + (string-match jao-mails-regexp + (gnus-extra-header 'BCc headers))) + "¬" ;; "~" + " ")))) + +(defconst jao-gnus--news-rx + (concat (regexp-opt '("ElDiaro.es " + "ElDiario.es - ElDiario.es: " + "The Guardian: " + "Aeon | a world of ideas: " + "Planet Debian: ")) + "\\|The Conversation – Articles (.+): " + "\\|unofficial mirror of [^:]+: " + "\\|[gq].+ updates on arXiv.org: ")) + +(defun gnus-user-format-function-f (headers) + (let* ((from (gnus-header-from headers)) + (from (gnus-summary-extract-address-component from)) + (from (replace-regexp-in-string jao-gnus--news-rx "" from))) + from)) + +(setq gnus-user-date-format-alist + '(((gnus-seconds-today) . "%H:%M") + ((+ 86400 (gnus-seconds-today)) . "'%H:%M") + ;; (604800 . "%a %H:%M") ;; that's one week + ((gnus-seconds-month) . "%a %d") + ((gnus-seconds-year) . "%b %d") + (t . "%b '%y"))) + +;;;; moving messages around +(defvar-local jao-gnus--spam-group nil) +(defvar-local jao-gnus--archiving-group nil) +(defvar-local jao-gnus--archive-as-copy-p nil) + +(defvar jao-gnus--last-move nil) +(defun jao-gnus-move-hook (a headers c to d) + (setq jao-gnus--last-move (cons to (mail-header-id headers)))) +(defun jao-gnus-goto-last-moved () + (interactive) + (when jao-gnus--last-move + (when (eq major-mode 'gnus-summary-mode) (gnus-summary-exit)) + (gnus-group-goto-group (car jao-gnus--last-move)) + (gnus-group-select-group) + (gnus-summary-goto-article (cdr jao-gnus--last-move) nil t))) +(add-hook 'gnus-summary-article-move-hook 'jao-gnus-move-hook) + +(defun jao-gnus-archive (follow) + (interactive "P") + (if jao-gnus--archiving-group + (progn + (if (or jao-gnus--archive-as-copy-p + (not (gnus-check-backend-function + 'request-move-article gnus-newsgroup-name))) + (gnus-summary-copy-article nil jao-gnus--archiving-group) + (gnus-summary-move-article nil jao-gnus--archiving-group)) + (when follow (jao-gnus-goto-last-moved))) + (gnus-summary-mark-as-read) + (gnus-summary-delete-article))) + +(defun jao-gnus-archive-tickingly () + (interactive) + (gnus-summary-tick-article) + (jao-gnus-archive) + (when jao-gnus--archive-as-copy-p + (gnus-summary-mark-as-read))) + +(defun jao-gnus-show-tickled () + (interactive) + (gnus-summary-limit-to-marks "!")) + +(make-variable-buffer-local + (defvar jao-gnus--trash-group nil)) + +(defun jao-gnus-trash () + (interactive) + (gnus-summary-mark-as-read) + (if jao-gnus--trash-group + (gnus-summary-move-article nil jao-gnus--trash-group) + (gnus-summary-delete-article))) + +(defun jao-gnus-move-to-spam () + (interactive) + (gnus-summary-mark-as-read) + (gnus-summary-move-article nil jao-gnus--spam-group)) + +(define-key gnus-summary-mode-map "Ba" 'jao-gnus-archive) +(define-key gnus-summary-mode-map "BA" 'jao-gnus-archive-tickingly) +(define-key gnus-summary-mode-map "Bl" 'jao-gnus-goto-last-moved) + +(define-key gnus-summary-mode-map (kbd "B DEL") 'jao-gnus-trash) +(define-key gnus-summary-mode-map (kbd "B <backspace>") 'jao-gnus-trash) +(define-key gnus-summary-mode-map "Bs" 'jao-gnus-move-to-spam) +(define-key gnus-summary-mode-map "/!" 'jao-gnus-show-tickled) +(define-key gnus-summary-mode-map [f7] 'gnus-summary-force-verify-and-decrypt) + +;;;; saving emails +(setq gnus-default-article-saver 'gnus-summary-save-article-mail) +(defvar jao-gnus-file-save-directory (expand-file-name "~/tmp")) +(defun jao-gnus-file-save (newsgroup headers &optional last-file) + (expand-file-name (format "%s.eml" (mail-header-subject headers)) + jao-gnus-file-save-directory)) +(setq gnus-mail-save-name 'jao-gnus-file-save) + +;;;; arXiv capture +(use-package org-capture + :config + (add-to-list 'org-capture-templates + '("X" "arXiv" entry (file "notes/physics/arxiv.org") + "* %:subject\n %i" :immediate-finish t) + t) + (org-capture-upgrade-templates org-capture-templates)) + +(defun jao-gnus-arXiv-capture () + (interactive) + (gnus-summary-select-article-buffer) + (gnus-article-goto-part 0) + (forward-paragraph) + (setq-local transient-mark-mode 'lambda) + (set-mark (point)) + (goto-char (point-max)) + (org-capture nil "X")) + +;;; article +;;;; config, headers +(setq mail-source-delete-incoming t) +(setq gnus-gcc-mark-as-read t) +(setq gnus-treat-display-smileys nil) +(setq gnus-treat-fill-long-lines nil) +(setq gnus-treat-fill-article 120) +(setq gnus-treat-fold-headers nil) +(setq gnus-treat-strip-leading-blank-lines t) +(setq gnus-article-auto-eval-lisp-snippets nil) +(setq gnus-posting-styles '((".*" (name "Jose A. Ortega Ruiz")))) +(setq gnus-single-article-buffer nil) +(setq gnus-article-update-lapsed-header 60) +(setq gnus-article-update-date-headers 60) + +(with-eval-after-load "gnus-art" + (setq gnus-visible-headers + (concat + gnus-visible-headers + "\\|^List-[iI][Dd]:\\|^X-Newsreader:\\|^X-Mailer:" + "\\|^User-Agent:\\|^X-User-Agent:\\|^X-RSS-Feed:"))) + +;;;; html and images +(setq gnus-button-url 'browse-url-generic + gnus-inhibit-images t + mm-discouraged-alternatives nil ;; '("text/html" "text/richtext") + mm-inline-large-images 'resize) + +(defvar-local jao-gnus--images nil) + +(defun jao-gnus--init-images () + (with-current-buffer gnus-article-buffer + (setq jao-gnus--images nil))) + +(add-hook 'gnus-select-article-hook #'jao-gnus--init-images) + +(defun jao-gnus-browse-html () + (interactive) + (let ((browse-url-browser-function jao-browse-url-external-function) + (browse-url-handlers nil) + (browse-url-default-handlers nil)) + (gnus-article-browse-html-article))) + +(defun jao-gnus-show-images () + (interactive) + (if window-system + (save-window-excursion + (gnus-summary-select-article-buffer) + (save-excursion + (if (and jao-afio-use-w3m (fboundp 'w3m-toggle-inline-images)) + (w3m-toggle-inline-images) + (setq jao-gnus--images (not jao-gnus--images)) + (if jao-gnus--images + (gnus-article-show-images) + (gnus-article-remove-images))))) + (jao-gnus-browse-html))) + +;;;; format from: + +(defvar jao-gnus--from-rx + (concat "From: \\\"?\\( *" jao-gnus--news-rx "\\)")) + +(defun jao-gnus-format-from () + (save-excursion + (goto-char (point-min)) + (when (re-search-forward jao-gnus--from-rx nil t) + (replace-match "" nil nil nil 1)))) + +(add-hook 'gnus-part-display-hook 'jao-gnus-format-from) + +;;;; follow links and enclosures +(defun jao-gnus-follow-link (&optional external) + (interactive "P") + (when (eq major-mode 'gnus-summary-mode) + (gnus-summary-select-article-buffer)) + (save-excursion + (goto-char (point-min)) + (when (or (search-forward-regexp "^Via: h" nil t) + (search-forward-regexp "^URL: h" nil t) + (and (search-forward-regexp "^Link$" nil t) + (not (beginning-of-line)))) + (if external + (jao-browse-with-external-browser) + (browse-url (jao-url-around-point)))))) + +(defun jao-gnus-open-enclosure () + (interactive) + (save-window-excursion + (gnus-summary-select-article-buffer) + (save-excursion + (goto-char (point-min)) + (let ((offset (or (and (search-forward-regexp "^Enclosure: " nil t) 2) + (and (search-forward-regexp "^Enclosure$" nil t) -2)))) + (when offset (forward-char offset)) + (if-let ((url (jao-url-around-point))) + (jao-mpc-add-or-play-url url) + (error "No enclosure found")))))) + +;;;; delayed messages +(require 'gnus-util) +(gnus-delay-initialize) +(setq gnus-delay-default-delay "3h") +(eval-after-load "message" + '(setq message-draft-headers (remove 'Date message-draft-headers))) + +;;; daemon and exit +(setq gnus-interactive-exit t) +(defun jao-quit-gnus () (gnus-group-exit) t) +(add-hook 'kill-emacs-query-functions #'jao-quit-gnus) + +;; daemon config +(setq mail-user-agent 'gnus-user-agent) +(setq gnus-asynchronous t) +(setq gnus-use-article-prefetch nil) +(setq gnus-save-killed-list nil) +(setq gnus-check-new-newsgroups nil) + +(require 'gnus-demon) + +(defun jao-gnus--scan () + (let ((inhibit-message t)) + (gnus-demon-scan-news) + (jao-gnus--notify))) + +(gnus-demon-add-handler 'jao-gnus--scan 5 1) + +;;; add-ons +;;;; notifications +;;;;; minibuffer +(defvar jao-gnus-tracked-groups + (let ((feeds (thread-first + (directory-files mail-source-directory nil "feeds\\.[^e]") + (seq-difference '("feeds.trove"))))) + `(("nnml:bigml\\.inbox" "B" jao-themes-f00) + ("nnml:bigml\\.alba" "A" jao-themes-f00) + ("nnml:bigml\\.bugs" "b" jao-themes-error) + ("nnml:bigml\\.support" "S" default) + ("nnml:jao\\.\\(inbox\\|trove\\)" "I" jao-themes-f01) + ("nnml:bigml\\.[^ibs]" "W" jao-themes-dimm) + ("nnml:jao.hacking" "H" jao-themes-dimm) + ("nnml:jao.[^isth]" "J" jao-themes-dimm) + (,(format "^nnml:%s" (regexp-opt feeds)) "F" jao-themes-dimm) + ("feeds\\.e" "E" jao-themes-dimm) + ("nnml:local" "l" jao-themes-dimm)))) + +(defun jao-gnus--unread-counts () + (seq-reduce (lambda (r g) + (let ((n (gnus-group-unread (car g)))) + (if (and (numberp n) (> n 0)) + (prog1 (cons (cons (car g) n) r) + (gnus-message 7 "%s in %s" n g)) + r))) + gnus-newsrc-alist + ())) + +(defun jao-gnus--unread-label (counts rx label face) + (let ((n (seq-reduce (lambda (n c) + (if (string-match-p rx (car c)) (+ n (cdr c)) n)) + counts + 0))) + (when (> n 0) `(:propertize ,(format "%s%d " label n) face ,face)))) + +(defvar jao-gnus--notify-strs ()) + +(defun jao-gnus--notify-strs () + (let ((counts (jao-gnus--unread-counts))) + (seq-filter #'identity + (seq-map (lambda (args) + (apply 'jao-gnus--unread-label counts args)) + jao-gnus-tracked-groups)))) + +(defun jao-gnus--notify () + (setq jao-gnus--notify-strs (jao-gnus--notify-strs)) + (jao-minibuffer-refresh)) + +(with-eval-after-load "jao-minibuffer" + (jao-minibuffer-add-variable 'jao-gnus--notify-strs -20)) + +(add-hook 'gnus-started-hook #'jao-gnus--notify) +(add-hook 'gnus-summary-exit-hook #'jao-gnus--notify) +(add-hook 'gnus-after-getting-new-news-hook #'jao-gnus--notify) + +;;;;; agenda updates +(add-hook 'gnus-summary-exit-hook #'jao-org-agenda) + + +;;;; open mail file in gnus +(defun jao-gnus-file-to-group (file &optional maildir newsdir m-server n-server) + "Compute the Gnus group name from the given file name. + IN: /home/jao/.emacs.d/gnus/Mail/jao.trove/32, /home/jao/.emacs.d/gnus/Mail/ + OUT: nnml:jao.trove " + (let* ((maildir (or maildir message-directory)) + (newsdir (or newsdir jao-gnus-leafnode-spool)) + (m-server (or m-server "nnml")) + (n-server (or n-server "nntp+localhost")) + (nntp (and newsdir (string-match-p newsdir file))) + (g (directory-file-name (file-name-directory file))) + (g (replace-regexp-in-string (file-name-as-directory maildir) "" g)) + (g (replace-regexp-in-string (file-name-as-directory newsdir) "" g)) + (g (cond (nntp (concat n-server ":" g)) + ((file-name-directory g) + (replace-regexp-in-string "^\\([^/]+\\)/" + (concat m-server ":\\1/") + (file-name-directory g) t)) + (t (concat m-server ":" g)))) + (g (replace-regexp-in-string "/" "." g)) + (g (replace-regexp-in-string "[/.]$" "" g))) + (cond ((string-match ":$" g) (concat g "inbox")) + (nntp g) + (t (replace-regexp-in-string ":\\." ":" g))))) + +(defun jao-gnus-goto-file (filename &optional _page) + (let ((group (jao-gnus-file-to-group filename)) + (id (file-name-nondirectory filename))) + (if (and group id) + (org-gnus-follow-link group id) + (message "Couldn't get relevant info for switching to Gnus.")))) + +;;;; afio +(defun jao-gnus--on-afio-switch () + (when (derived-mode-p 'gnus-group-mode) + (let ((no (or (gnus-group-unread (gnus-group-group-name)) 0))) + (unless (> no 0) (gnus-group-first-unread-group))))) + +(add-hook 'jao-afio-switch-hook #'jao-gnus--on-afio-switch) + +(defun jao-gnus-refresh-workspace () + (interactive) + (save-window-excursion (calendar) (jao-org-agenda))) + +;;;; gnus-icalendar +(require 'ol-gnus) +(use-package gnus-icalendar + :demand t + :init (setq gnus-icalendar-org-capture-file + (expand-file-name "inbox.org" org-directory) + gnus-icalendar-org-capture-headline '("Appointments")) + :config (gnus-icalendar-org-setup)) + +;;;; bbdb +(with-eval-after-load "bbdb" + ;; (bbdb-initialize 'gnus 'message 'pgp) + (bbdb-mua-auto-update-init 'gnus) + (with-eval-after-load "gnus-sum" + (define-key gnus-summary-mode-map ":" 'bbdb-mua-annotate-sender) + (define-key gnus-summary-mode-map ";" 'bbdb-mua-annotate-recipients))) + +;;;; randomsig +(with-eval-after-load "randomsig" + (with-eval-after-load "gnus-sum" + (define-key gnus-summary-save-map "-" 'gnus/randomsig-summary-read-sig))) + +;;;; recoll +(unless jao-notmuch-enabled + (with-eval-after-load "org" + (org-link-set-parameters "message" :follow #'jao-gnus-goto-file)) + (with-eval-after-load "consult-recoll" + (add-to-list 'consult-recoll-open-fns + '("message/rfc822" . jao-gnus-goto-file)))) +;;; keyboard shortcuts +(define-key gnus-article-mode-map "i" 'jao-gnus-show-images) +(define-key gnus-summary-mode-map "i" 'jao-gnus-show-images) +(define-key gnus-article-mode-map "\M-g" 'jao-gnus-follow-link) +(define-key gnus-summary-mode-map "\M-g" 'jao-gnus-follow-link) +(define-key gnus-summary-mode-map "v" 'scroll-other-window) +(define-key gnus-summary-mode-map "V" 'scroll-other-window-down) +(define-key gnus-summary-mode-map "X" 'jao-gnus-arXiv-capture) +(define-key gnus-summary-mode-map "e" 'jao-gnus-open-enclosure) +(define-key gnus-summary-mode-map "\C-l" nil) +(define-key gnus-group-mode-map "a" 'jao-gnus-refresh-workspace) |