;;; bandali-gnus.el --- bandali's Gnus setup         -*- lexical-binding: t; -*-

;; Copyright (C) 2018-2021  Amin Bandali

;; Author: Amin Bandali <bandali@gnu.org>
;; Keywords: mail, news

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;; My trusty super awesome Gnus setup.

;;; Code:

(defvar b/maildir (expand-file-name "~/mail/"))
(with-eval-after-load 'recentf
  (add-to-list 'recentf-exclude b/maildir))

(defvar b/gnus-init-file (b/etc "gnus"))

(eval-when-compile
  (progn
    (defvar nndraft-directory)
    (defvar gnus-read-newsrc-file)
    (defvar gnus-save-newsrc-file)
    (defvar gnus-gcc-mark-as-read)
    (defvar nnmail-split-abbrev-alist)))

(declare-function article-make-date-line "gnus-art" (date type))

(csetq
 mail-user-agent 'gnus-user-agent
 read-mail-command 'gnus

 gnus-select-method '(nnnil "")
 gnus-secondary-select-methods
 '((nnimap "shemshak"
           (nnimap-stream plain)
           (nnimap-address "127.0.0.1")
           (nnimap-server-port 143)
           (nnimap-authenticator plain)
           (nnimap-user "bandali@shemshak.local"))
   (nnimap "gnu"
           (nnimap-stream plain)
           (nnimap-address "127.0.0.1")
           (nnimap-server-port 143)
           (nnimap-authenticator plain)
           (nnimap-user "bandali@gnu.local")
           (nnimap-inbox "INBOX")
           (nnimap-split-methods 'nnimap-split-fancy)
           (nnimap-split-fancy (|
                                ;; (: gnus-registry-split-fancy-with-parent)
                                ;; (: gnus-group-split-fancy "INBOX" t "INBOX")
                                ;; keep debbugs emails in INBOX
                                (list ".*<\\(.*\\)\\.debbugs\\.gnu\\.org>.*" "INBOX")
                                ;; list moderation emails
                                (from ".+-\\(owner\\|bounces\\)@\\(non\\)?gnu\\.org" "listmod")
                                ;; gnu
                                (list ".*<\\(.*\\)\\.\\(non\\)?gnu\\.org>.*" "l.\\1")
                                ;; gnus
                                (list ".*<\\(.*\\)\\.gnus\\.org>.*" "l.\\1")
                                ;; libreplanet
                                (list ".*<\\(.*\\)\\.libreplanet\\.org>.*" "l.\\1")
                                ;; iana (e.g. tz-announce)
                                (list ".*<\\(.*\\)\\.iana\\.org>.*" "l.\\1")
                                ;; haskell
                                (list ".*<\\(.*\\)\\.haskell\\.org>.*" "l.\\1")
                                ;; *.lists.sr.ht, omitting one dot if present
                                ;;    add more \\.?\\([^.]*\\) if needed
                                (list ".*<~\\(.*\\)/\\([^.]*\\)\\.?\\([^.]*\\)\\.lists\\.sr\\.ht>.*" "l.~\\1.\\2\\3")
                                ;; webmasters
                                (from "webmasters\\(-comment\\)?@gnu\\.org" "webmasters")
                                ;; other
                                ("subject" "nagios-fsf:.*" "nagios-fsf")
                                (list ".*atreus.freelists.org" "l.atreus")
                                (list ".*deepspec.lists.cs.princeton.edu" "l.deepspec")
                                ;; (list ".*haskell-art.we.lurk.org" "l.haskell.art") ;d
                                ;; (list ".*notmuch.notmuchmail.org" "l.notmuch") ;u
                                (list ".*dev.lists.parabola.nu" "l.parabola-dev")
                                ;; ----------------------------------
                                ;; legend: (u)nsubscribed | (d)ead
                                ;; ----------------------------------
                                ;; spam
                                ("X-Spam_action" "reject" "Junk")
                                ;; otherwise, leave mail in INBOX
                                "INBOX")))
   (nnimap "uwaterloo"
           (nnimap-stream plain)
           (nnimap-address "127.0.0.1")
           (nnimap-server-port 143)
           (nnimap-authenticator plain)
           (nnimap-user "abandali@uwaterloo.local")
           (nnimap-inbox "INBOX")
           (nnimap-split-methods 'nnimap-split-fancy)
           (nnimap-split-fancy (|
                                ;; (: gnus-registry-split-fancy-with-parent)
                                ;; se212-f19
                                ("subject" "SE\\s-?212" "course.se212-f19")
                                (from "SE\\s-?212" "course.se212-f19")
                                ;; catch-all
                                "INBOX")))
   (nnimap "csc"
           (nnimap-stream plain)
           (nnimap-address "127.0.0.1")
           (nnimap-server-port 143)
           (nnimap-authenticator plain)
           (nnimap-user "abandali@csclub.uwaterloo.local")
           (nnimap-inbox "INBOX")
           (nnimap-split-methods 'nnimap-split-fancy)
           (nnimap-split-fancy (|
                                ;; cron reports and other messages from root
                                (from "root@\\(.*\\.\\)?csclub\\.uwaterloo\\.ca" "INBOX")
                                ;; spam
                                ("X-Spam-Flag" "YES" "Junk")
                                ;; catch-all
                                "INBOX")))
   (nnimap "sfl"
           (nnimap-stream plain)
           (nnimap-address "127.0.0.1")
           (nnimap-server-port 143)
           (nnimap-authenticator plain)
           (nnimap-user "amin.bandali@savoirfairelinux.local")))
 gnus-message-archive-group "nnimap+gnu:INBOX"
 gnus-parameters
 '(("l\\.deepspec"
    (to-address . "deepspec@lists.cs.princeton.edu")
    (to-list    . "deepspec@lists.cs.princeton.edu")
    (list-identifier . "\\[deepspec\\]"))
   ("l\\.fencepost-users"
    (to-address . "fencepost-users@gnu.org")
    (to-list    . "fencepost-users@gnu.org")
    (list-identifier . "\\[Fencepost-users\\]"))
   ("l\\.haskell-cafe"
    (to-address . "haskell-cafe@haskell.org")
    (to-list    . "haskell-cafe@haskell.org")
    (list-identifier . "\\[Haskell-cafe\\]"))
   ("gnu.*"
    (gcc-self . t))
   ;; ("l\\."
   ;;  (subscribed . t))
   ("nnimap\\+uwaterloo:.*"
    (gcc-self . t)))
 ;; nnimap-record-commands t
 ;; gnus-large-newsgroup  50
 gnus-home-directory   (b/var "gnus/")
 gnus-directory        (concat gnus-home-directory "news/")
 message-directory     (concat gnus-home-directory "mail/")
 nndraft-directory     (concat gnus-home-directory "drafts/")
 gnus-save-newsrc-file nil
 gnus-read-newsrc-file nil
 gnus-search-use-parsed-queries t
 gnus-interactive-exit nil
 gnus-gcc-mark-as-read t)

(with-eval-after-load 'gnus
  (when (version< emacs-version "27")
    (with-eval-after-load 'nnmail
      (add-to-list
       'nnmail-split-abbrev-alist
       '(list . "list-id\\|list-post\\|x-mailing-list\\|x-beenthere\\|x-loop")
       t)))

  ;; (require 'gnus-registry)
  ;; (setq gnus-registry-max-entries 2500)
  ;; (setq gnus-registry-ignored-groups
  ;;       (append gnus-registry-ignored-groups
  ;;               '(("^nnimap:gnu\\.l" t)
  ;;                 ("webmasters$" t))))
  ;; (gnus-registry-initialize)

  (with-eval-after-load 'recentf
    (add-to-list 'recentf-exclude gnus-home-directory))

  ;; hooks
  (add-hook 'gnus-group-mode-hook #'gnus-topic-mode)
  (add-hook 'gnus-group-mode-hook #'gnus-agent-mode))
;; global key bindings
(global-set-key (kbd "C-c m") #'gnus-plugged)
(global-set-key (kbd "C-c M") #'gnus-unplugged)

(with-eval-after-load 'gnus-art
  (csetq
   gnus-buttonized-mime-types '("multipart/\\(signed\\|encrypted\\)")
   gnus-sorted-header-list '("^From:"
                             "^X-RT-Originator"
                             "^Newsgroups:"
                             "^Subject:"
                             "^Date:"
                             "^Envelope-To:"
                             "^Followup-To:"
                             "^Reply-To:"
                             "^Organization:"
                             "^Summary:"
                             "^Abstract:"
                             "^Keywords:"
                             "^To:"
                             "^[BGF]?Cc:"
                             "^Posted-To:"
                             "^Mail-Copies-To:"
                             "^Mail-Followup-To:"
                             "^Apparently-To:"
                             "^Resent-From:"
                             "^User-Agent:"
                             "^X-detected-operating-system:"
                             "^X-Spam_action:"
                             "^X-Spam_bar:"
                             "^Message-ID:"
                             ;; "^References:"
                             "^List-Id:"
                             "^Gnus-Warning:")
   gnus-visible-headers (mapconcat 'identity
                                   gnus-sorted-header-list
                                   "\\|")
  ;; local-lapsed article dates
  ;; from https://www.emacswiki.org/emacs/GnusFormatting#toc11
  gnus-article-date-headers '(user-defined)
  gnus-article-time-format
  (lambda (time)
    (let* ((date (format-time-string "%a, %d %b %Y %T %z" time))
           (local (article-make-date-line date 'local))
           (combined-lapsed (article-make-date-line date
                                                    'combined-lapsed))
           (lapsed (progn
                     (string-match " (.+" combined-lapsed)
                     (match-string 0 combined-lapsed))))
      (concat local lapsed))))
  ;; local key bindings
  (declare-function org-store-link "ol" (arg &optional interactive?))
  (define-key gnus-article-mode-map (kbd "M-L") #'org-store-link))

(with-eval-after-load 'gnus-sum
  (csetq gnus-thread-sort-functions '(gnus-thread-sort-by-number
                                      gnus-thread-sort-by-subject
                                      gnus-thread-sort-by-date))
  ;; local key bindings
  (define-key gnus-summary-mode-map (kbd "M-L") #'org-store-link)
  (define-key gnus-summary-mode-map (kbd "r")
    #'gnus-summary-reply-with-original)
  (define-key gnus-summary-mode-map (kbd "R")
    #'gnus-summary-wide-reply-with-original)
  (defvar b/gnus-summary-prefix-map)
  (define-prefix-command 'b/gnus-summary-prefix-map)
  (define-key gnus-summary-mode-map (kbd "v")
    'b/gnus-summary-prefix-map)
  (define-key b/gnus-summary-prefix-map (kbd "r")
    #'gnus-summary-reply)
  (define-key b/gnus-summary-prefix-map (kbd "w")
    #'gnus-summary-wide-reply)
  (define-key b/gnus-summary-prefix-map (kbd "v")
    #'gnus-summary-show-raw-article))
;; hooks
(add-hook 'gnus-summary-mode-hook #'b/no-mouse-autoselect-window)

(defvar b/sfl-p nil)
(with-eval-after-load 'gnus-msg
  (defvar b/shemshak-signature "Amin Bandali
https://shemshak.org/~bandali")
  (defvar b/uwaterloo-signature "Amin Bandali, MMath
https://shemshak.org/~bandali")
  (defvar b/csc-signature "Amin Bandali (https://shemshak.org/~bandali)
Systems Committee <syscom@csclub.uwaterloo.ca>
Computer Science Club of the University of Waterloo")
  (defvar b/sfl-signature "Amin Bandali
Free Software Consultant
Savoir-faire Linux
jami:bandali")
  (csetq
   gnus-message-replysign t
   gnus-posting-styles
   '((".*"
      (address "bandali@gnu.org")
      ("X-Message-SMTP-Method" "smtp fencepost.gnu.org 587"))
     ("nnimap\\+gnu:l\\..*"
      (signature nil))
     ((header "subject" "ThankCRM")
      (to "webmasters-comment@gnu.org")
      (body "")
      (eval (setq b/message-cite-say-hi nil)))
     ("nnimap\\+shemshak:.*"
      (address "amin@shemshak.org")
      ("X-Message-SMTP-Method" "smtp mail.shemshak.org 587")
      (body "\nBest,\n")
      (signature b/shemshak-signature)
      (gcc "nnimap+shemshak:Sent")
      (eval (setq b/message-cite-say-hi t)))
     ("nnimap\\+uwaterloo:.*"
      (address "bandali@uwaterloo.ca")
      ("X-Message-SMTP-Method" "smtp connect.uwaterloo.ca 587")
      (body "\nBest,\n")
      (signature b/uwaterloo-signature))
     ("nnimap\\+uwaterloo:INBOX"
      (gcc "\"nnimap+uwaterloo:Sent Items\""))
     ("nnimap\\+csc:.*"
      (address "bandali@csclub.uwaterloo.ca")
      ("X-Message-SMTP-Method" "smtp mail.csclub.uwaterloo.ca 587")
      (signature b/csc-signature)
      (gcc "nnimap+csc:Sent"))
     ("nnimap\\+sfl:.*"
      (address "amin.bandali@savoirfairelinux.com")
      (signature b/sfl-signature)
      ("X-Message-SMTP-Method" "smtp mail.savoirfairelinux.com 587")
      (gcc "nnimap+sfl:Sent")
      (eval (setq-local b/sfl-p t))))))
;; hooks
;; (with-eval-after-load 'gnus
;;   (add-hook 'gnus-message-setup-hook
;;             (lambda ()
;;               (unless (or (mml-secure-is-encrypted-p)
;;                           b/sfl-p)
;;                 (mml-secure-message-sign)))))

(with-eval-after-load 'gnus-topic
  (csetq
   gnus-topic-line-format "%i[ %A: %(%{%n%}%) ]%v\n"
   gnus-topic-topology '(("Gnus" visible nil nil)
                         (("misc" visible nil nil))
                         (("sfl" visible nil nil))
                         (("csc" visible nil nil))
                         (("uwaterloo" visible nil nil))
                         (("shemshak" visible nil nil))
                         (("gnu" visible nil nil))
                         (("old-gnu" visible nil nil)))))

(with-eval-after-load 'gnus-agent
  (csetq gnus-agent-synchronize-flags 'ask))

(with-eval-after-load 'gnus-group
  (csetq gnus-permanently-visible-groups "\\(:INBOX$\\|:gnu$\\)"))

(with-eval-after-load 'gnus-win
  (csetq gnus-use-full-window nil))

(with-eval-after-load 'gnus-dired
  (add-hook 'dired-mode-hook 'gnus-dired-mode))

;; (with-eval-after-load 'gnus-utils
;;   (csetq gnus-completing-read-function 'gnus-ido-completing-read))

(with-eval-after-load 'mm-decode
  (csetq mm-discouraged-alternatives '("text/html" "text/richtext")
         mm-decrypt-option 'known
         mm-verify-option 'known))

(with-eval-after-load 'mm-uu
  (when (version< "27" emacs-version)
    (set-face-attribute 'mm-uu-extract nil :extend t))
  (when (version< emacs-version "27")
    (csetq mm-uu-diff-groups-regexp ".")))

(with-eval-after-load 'mml-sec
  (csetq mml-secure-openpgp-encrypt-to-self t
         mml-secure-openpgp-sign-with-sender t))

;; (require 'gnus-article-treat-patch)
;; ;; note: be sure to customize faces with `:foreground "white"' when
;; ;; using a theme with a white/light background :)
;; (setq ft/gnus-article-patch-conditions
;;       '("^@@ -[0-9]+,[0-9]+ \\+[0-9]+,[0-9]+ @@"))

(provide 'bandali-gnus)
;;; bandali-gnus.el ends here