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

;; Copyright (C) 2018-2022  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))

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

 gnus-select-method '(nnnil "")
 gnus-secondary-select-methods
 `(,@(cond
      ((string= (system-name) "langa")
       '((nnimap
          "kelar"
          (nnimap-stream plain)
          (nnimap-address "127.0.0.1")
          (nnimap-server-port 143)
          (nnimap-authenticator plain)
          (nnimap-user "bandali@kelar.local"))
         (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")
            ;; spam
            ("X-Spam_action" "reject" "Junk")
            ;; 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")
            ;; fsf
            (list ".*<\\(.*\\)\\.fsf\\.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")
            ;; mailop
            (list ".*<\\(.*\\)\\.mailop\\.org>.*" "l.\\1")
            ;; sdlu
            (list ".*<\\(.*\\)\\.spammers\\.dontlike\\.us>.*" "l.sdlu")
            ;; bitfolk
            (from ".*@\\(.+\\)?bitfolk\\.com>.*" "bitfolk")
            ;; 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
            ;; ----------------------------------
            ;; 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")))))
      ((string= (system-name) "jirud")
       '((nnimap
         "sfl"
         (nnimap-stream tls)
         (nnimap-address "mail.savoirfairelinux.com")
         (nnimap-user "amin.bandali"))))))
 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-process-mark-toggle t
 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 g") #'gnus-plugged)
(global-set-key (kbd "C-c G") #'gnus-unplugged)

(with-eval-after-load 'gnus-art
  (setq
   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
  (setq 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")
  (setq
   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\\+kelar:.*"
      (address "bandali@kelar.org")
      ("X-Message-SMTP-Method" "smtp mail.kelar.org 587")
      (body "\nBest,\n")
      (gcc "nnimap+kelar:Sent")
      (eval (setq b/message-cite-say-hi t)))
     ("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
  (setq
   gnus-topic-line-format "%i[ %A: %(%{%n%}%) ]%v\n"
   gnus-topic-topology
   `(("Gnus" visible nil nil)
     (("misc" visible nil nil))
     ,@(cond
        ((string= (system-name) "jirud")
         '((("sfl" visible nil nil))))
        ((string= (system-name) "langa")
         '((("csc" visible nil nil))
           (("uwaterloo" visible nil nil))
           (("kelar" visible nil nil))
           (("shemshak" visible nil nil))
           (("gnu" visible nil nil))
           (("old-gnu" visible nil nil))))))))

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

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

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

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

(with-eval-after-load 'mm-decode
  (setq
   ;; mm-attachment-override-types `("text/x-diff" "text/x-patch"
   ;;                                ,@mm-attachment-override-types)
   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")
    (setq mm-uu-diff-groups-regexp ".")))

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

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