diff options
author | jao <jao@gnu.org> | 2021-04-18 02:55:22 +0100 |
---|---|---|
committer | jao <jao@gnu.org> | 2021-04-18 02:56:13 +0100 |
commit | 7d8344ac8af19d7a88c3547fa0ab8ced01135e86 (patch) | |
tree | 490eb7b83a077b59ac51f3ca7c0e7245d7d17173 | |
parent | 8ea756c5d0ff541d33fb1be56d4c53a0b12f90e4 (diff) | |
download | elibs-7d8344ac8af19d7a88c3547fa0ab8ced01135e86.tar.gz elibs-7d8344ac8af19d7a88c3547fa0ab8ced01135e86.tar.bz2 |
email.org, with some notmuch fine-tuning
-rw-r--r-- | email.org | 448 | ||||
-rw-r--r-- | eww.org | 1 | ||||
-rw-r--r-- | gnus.org | 22 | ||||
-rw-r--r-- | init.org | 461 | ||||
-rw-r--r-- | lib/eos/jao-afio.el | 21 | ||||
-rw-r--r-- | lib/themes/jao-themes.el | 16 | ||||
-rw-r--r-- | readme.org | 1 |
7 files changed, 503 insertions, 467 deletions
diff --git a/email.org b/email.org new file mode 100644 index 0000000..f3e23c5 --- /dev/null +++ b/email.org @@ -0,0 +1,448 @@ +#+title: email handling (message mode, bbdb, notmuch, et al.) + +* Message mode +*** Customization + #+begin_src emacs-lisp + (defvar jao-mails "") + (defvar jao-mails-regexp) + (setq jao-mails-regexp (regexp-opt jao-mails)) + + (require 'message) + (setq message-send-mail-function 'message-send-mail-with-sendmail + message-sendmail-envelope-from 'header + message-sendmail-f-is-evil nil) + (setq imap-store-password t) + (setq password-cache-expiry nil) + (setq message-generate-headers-first t) + (setq message-forward-before-signature nil) + (setq message-alternative-emails jao-mails-regexp) + (setq message-dont-reply-to-names + (format "%s\\|%s" jao-mails-regexp (regexp-opt '("noreply@" "@noreply" + "no-reply@" "@no-reply" + "notifications@github")))) + (setq message-citation-line-format "On %a, %b %d %Y, %N wrote:\n") + (setq message-citation-line-function 'message-insert-formatted-citation-line) + + (setq message-user-fqdn "gnus.jao.io") + + ;; writing messages + (setq message-kill-buffer-on-exit t) + (setq message-max-buffers 5) + (setq message-insert-signature t) + (setq message-from-style 'angles + user-mail-address (car jao-mails) + mail-host-address system-name + message-syntax-checks '((sender . disabled)) + message-default-headers + (concat + "X-Attribution: jao\n" + "X-Clacks-Overhead: GNU Terry Pratchett\n" + "X-URL: <http://jao.io/>\n") + message-hidden-headers + '("^References:" "^Face:" "^X-Face:" "^X-Draft-From:") + message-make-forward-subject-function 'message-forward-subject-fwd) + + (setq message-expand-name-standard-ui t) + #+end_src +*** Encryption + #+BEGIN_SRC emacs-lisp + (require 'gnutls) + ;; avoiding bogus warning + (setq gnutls-min-prime-bits nil) + (setq gnus-buttonized-mime-types + '("multipart/encrypted" "multipart/signed" "multipart/alternative")) + + (setq mm-verify-option 'always) + (setq mm-decrypt-option 'always) + + (setq mm-sign-option 'guided) + (setq mm-encrypt-option 'guided) + + (setq mml-secure-passphrase-cache-expiry (* 3600 24) + password-cache-expiry (* 3600 24)) + + (setq smime-CA-directory "/etc/ssl/certs/" + smime-certificate-directory"/home/jao/.emacs.d/gnus/Mail/certs/") + + ;; Tells Gnus to inline the part + (eval-after-load "mm-decode" + '(add-to-list 'mm-inlined-types "application/pgp$")) + ;; Tells Gnus how to display the part when it is requested + (eval-after-load "mm-decode" + '(add-to-list 'mm-inline-media-tests '("application/pgp$" + mm-inline-text identity))) + ;; Tell Gnus not to wait for a request, just display the thing + ;; straight away. + (eval-after-load "mm-decode" + '(add-to-list 'mm-automatic-display "application/pgp$")) + ;; But don't display the signatures, please. + (eval-after-load "mm-decode" + (quote (setq mm-automatic-display (remove "application/pgp-signature" + mm-automatic-display)))) + + ;; decide whether to encrypt or just sign outgoing messages + (defvar jao-message-try-sign nil) + (defun jao-message-maybe-sign () + (when (and jao-message-try-sign (y-or-n-p "Sign message? ")) + (if (y-or-n-p "Encrypt message? ") + (let ((recipient (message-fetch-field "To"))) + (if (or (pgg-lookup-key recipient) + (and (y-or-n-p (format "Fetch %s's key? " recipient)) + (pgg-fetch-key pgg-default-keyserver-address + recipient))) + (mml-secure-message-encrypt-pgp) + (mml-secure-message-sign-pgp))) + (mml-secure-message-sign-pgp)))) + + ;; for ma gnus + (eval-after-load "rfc2047" + '(add-to-list 'rfc2047-header-encoding-alist + '("User-Agent" . address-mime))) + + ;; (define-key message-mode-map [f7] 'mml-secure-message-sign-pgp) + (define-key message-mode-map [f8] 'mml-secure-message-encrypt-pgp) + #+END_SRC +*** Attach image to message + Use ~C-c C-p~ in message-mode. +*** Check attachment + #+BEGIN_SRC emacs-lisp + (defvar jao-message-attachment-regexp "\\([Ww]e send\\|[Ii] send\\|attach\\)") + (defun jao-message-check-attachment () + "Check if there is an attachment in the message if I claim it." + (save-excursion + (message-goto-body) + (when (search-forward-regexp jao-message-attachment-regexp nil t nil) + (message-goto-body) + (unless (or (search-forward "<#part" nil t nil) + (message-y-or-n-p + "No attachment. Send the message? " nil nil)) + (error "No message sent"))))) + #+END_SRC +*** Check Gcc + #+BEGIN_SRC emacs-lisp + (defun jao-message-check-gcc () + "Ask whether to keep a copy of message." + (save-excursion + (save-restriction + (message-narrow-to-headers) + (when (and (message-fetch-field "Gcc") + (not (y-or-n-p "Archive? "))) + (message-remove-header "Gcc"))))) + + (defun jao-message-toggle-gcc () + "Insert or remove the \"Gcc\" header." + (interactive) + (save-excursion + (save-restriction + (message-narrow-to-headers) + (if (message-fetch-field "Gcc") + (message-remove-header "Gcc") + (gnus-inews-insert-gcc))))) + + ;; (define-key message-mode-map [(f6)] 'jao-message-toggle-gcc) + #+END_SRC +*** Check recipient + #+begin_src emacs-lisp + (defun jao-message-check-recipient () + (save-excursion + (save-restriction + (message-narrow-to-headers) + (when-let ((to (message-fetch-field "To"))) + (when (string-match-p jao-mails-regexp to) + (unless (y-or-n-p "Message is addressed to yourself. Continue?") + (error "Message not sent"))))))) + #+end_src +*** Randomsig + #+BEGIN_SRC emacs-lisp + (when (require 'randomsig nil t) + (define-key message-mode-map (kbd "C-c s") 'randomsig-replace-sig) + (define-key message-mode-map (kbd "C-c S") 'randomsig-select-sig) + (eval-after-load "gnus-sum" + '(define-key gnus-summary-save-map "-" 'gnus/randomsig-summary-read-sig)) + (setq randomsig-dir (expand-file-name "~/etc/config/emacs")) + (setq randomsig-files '("signatures.txt")) + ;; or (setq randomsig-files (randomsig-search-sigfiles)) + ;; or (setq randomsig-files 'randomsig-search-sigfiles) + (setq message-signature 'randomsig-signature) + (setq randomsig-delimiter-pattern "^%$" + randomsig-delimiter "%")) + #+END_SRC +*** Send mail hooks + #+BEGIN_SRC emacs-lisp + (dolist (h '(jao-message-check-gcc + jao-message-check-attachment + jao-message-check-recipient + jao-message-maybe-sign)) + (add-hook 'message-send-hook h)) + #+END_SRC +* sendmail/smtp + #+BEGIN_SRC emacs-lisp + (require 'smtpmail) + + (defun jao-sendmail-gmail () + (setq smtpmail-auth-supported '(login cram-md5 plain)) + (setq smtpmail-smtp-server "smtp.gmail.com") + (setq smtpmail-smtp-service 587)) + + (defun jao-sendmail-local () + (setq smtpmail-auth-supported nil) ;; (cram-md5 plain login) + (setq smtpmail-smtp-server "127.0.0.1") + (setq smtpmail-smtp-service 25)) + + ;; (jao-sendmail-gmail) + (jao-sendmail-local) + #+END_SRC +* notmuch +*** Package configuration + #+begin_src emacs-lisp + (defun jao-notmuch--mboxes-search (box) + (mapcar (lambda (m) + `(:name ,m + :search-type tree + :query ,(format "folder:%s/%s and tag:unread" box m))) + (jao-list-mailboxes box))) + + (use-package notmuch + :ensure t + :init + (setq notmuch-fcc-dirs '(("jao@bigml.com" . "bigml/sent") + (".*" . "jao/sent")) + notmuch-message-headers-visible t + notmuch-message-headers + '("Subject" "To" "Cc" "Date" "List-Id" + "X-Mailer" "User-Agent" "X-User-Agent") + notmuch-tagging-keys + '(("a" notmuch-archive-tags "Archive") + ("u" notmuch-show-mark-read-tags "Mark read") + ("f" ("+flagged") "Flag") + ("s" ("+spam" "-inbox") "Mark as spam") + ("d" ("+deleted" "-inbox") "Delete")) + notmuch-saved-searches + `((:name "jao" :key "j" + :query "folder:jao/inbox" + :search-type tree + :count-query "folder:jao/inbox and tag:unread") + (:name "bigml" :key "b" + :count-query "folder:bigml/inbox and tag:unread" + :search-type tree + :query "folder:bigml/inbox") + ,@(jao-notmuch--mboxes-search "jao") + ,@(jao-notmuch--mboxes-search "bigml") + ,@(jao-notmuch--mboxes-search "feeds") + (:name "unread" :query "tag:unread" :key "u" :search-type tree) + (:name "sent" :query "tag:sent" :key "t" :search-type tree) + (:name "drafts" :query "tag:draft" :key "d" :search-type tree) + (:name "all mail" :query "*" :count-query "tag:unread" :key "a" + :search-type tree)) + notmuch-hello-sections + '(notmuch-hello-insert-saved-searches + notmuch-hello-insert-alltags + notmuch-hello-insert-header + ;; notmuch-hello-insert-footer + ) + notmuch-show-all-multipart/alternative-parts nil + notmuch-show-all-tags-list t + notmuch-show-indent-messages-width 2 + notmuch-show-part-button-default-action 'notmuch-show-view-part + notmuch-show-logo nil + notmuch-show-empty-saved-searches nil + notmuch-tree-result-format + '(("date" . "%12s ") + ("authors" . "%-30s") + ("subject" . " - %-90s") + ("tags" . " (%s)")) + notmuch-unthreaded-result-format notmuch-tree-result-format + notmuch-wash-wrap-lines-length 80) + :bind (:map notmuch-show-mode-map + ("C-c C-c" . jao-notmuch-goto-message-in-gnus))) + #+end_SRC +* gnus +*** notmuch -> gnus + #+begin_src emacs-lisp + (defun jao-notmuch-goto-message-in-gnus () + "Open a summary buffer containing the current notmuch article." + (interactive) + (let ((group (jao-maildir-file-to-group (notmuch-show-get-filename))) + (message-id (replace-regexp-in-string "^id:" + "" + (notmuch-show-get-message-id)))) + (if (and group message-id) + (org-gnus-follow-link group message-id) + (message "Couldn't get relevant infos for switching to Gnus.")))) + #+end_src +*** gnus.el tangling + The core of Gnus's configuration is tangled to the usual ~gnus.el~ + from [[./gnus.org][gnus.org]]. + #+begin_src emacs-lisp + (defalias 'jao-open-gnus-frame 'jao-afio--goto-mail) + (setq gnus-init-file (jao-maybe-tangle "gnus")) + #+end_src +*** Directories + #+begin_src emacs-lisp + (setq gnus-home-directory "~/.emacs.d/gnus" + gnus-directory gnus-home-directory) + + (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-auto-save-directory (jao-gnus-dir "Mail/drafts/") + 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")) + #+end_src +* Visual message fill column + #+begin_src emacs-lisp + (use-package visual-fill-column + :ensure t + :init + (setq-default fringes-outside-margins nil + visual-fill-column-width 80 + visual-fill-column-fringes-outside-margins nil) + (setq gnus-treat-fill-long-lines nil) + :config + (setq split-window-preferred-function + #'visual-fill-column-split-window-sensibly) + :bind ((:map ctl-x-map ("M-f" . visual-fill-column-mode)))) + + ;; (add-hook 'gnus-article-mode-hook #'visual-line-mode) + ;; (add-hook 'gnus-article-mode-hook #'visual-fill-column-mode) + + ;; Name may be misleading, it does not set `fill-column' (which + ;; is still used by M-q) in `message-mode', but enables + ;; auto-filling on a given column. + ;; (setq message-fill-column nil) + ;; FIXME: There is no proper way to make fill commands to unfill. + ;; (add-hook 'message-mode-hook + ;; (lambda () + ;; (setq-local fill-column most-positive-fixnum))) + ;; (when-require 'visual-fill-column + ;; (add-hook 'message-mode-hook #'visual-fill-column-mode)) + #+end_src +* Mail this buffer + #+BEGIN_SRC emacs-lisp + (defun jao-mail-this-file () + (interactive) + (let ((file (buffer-file-name))) + (compose-mail) + (save-excursion + (message-goto-subject) + (insert (file-name-nondirectory file)) + (message-goto-body) + (newline 2) + (mml-attach-file file (mm-default-file-encoding file))))) + #+END_SRC +* mailcap + #+BEGIN_SRC emacs-lisp + (require 'mailcap) + + (add-to-list 'mailcap-mime-extensions '(".JPEG" . "image/jpeg")) + (add-to-list 'mailcap-mime-extensions '(".JPG" . "image/jpeg")) + + (let* ((apps (cdr (assoc "application" mailcap-mime-data))) + (apps (cl-remove-if (lambda (x) (string= (car x) "pdf")) apps))) + (setcdr (assoc "application" mailcap-mime-data) apps) + (mailcap-parse-mailcaps nil t)) + #+END_SRC +* frm + #+begin_src emacs-lisp + (use-package jao-frm + :init (setq jao-frm-mail-command 'jao-open-gnus-frame)) + + (defun jao-frm--formatter (mbox n) + (apply #'format "%s/%s: %s" `(,@(last (split-string mbox "/") 2) ,n))) + + (defun jao-frm--show () + (interactive) + (jao-frm-show-mail-numbers #'jao-frm--formatter)) + + (global-set-key [(f12)] 'jao-frm--show) + (global-set-key [(f8)] 'jao-frm) + + #+end_src +* maildirs + #+begin_src emacs-lisp + (defun jao-list-mailboxes (base) + (let ((dir (expand-file-name base "~/var/mail"))) + (cl-remove-if (lambda (x) + (member x '("." ".." "sent" "inbox" "trash"))) + (directory-files dir)))) + + (defvar jao-maildir-maildirs nil) + (defvar jao-maildir-tracked-maildirs nil) + (use-package jao-maildir + :config + (defun jao-maildir--ensure-counts () + (when gnus-newsgroup-name + (when (string-match "^nnimap.*:\\(.+\\)" gnus-newsgroup-name) + (let ((mbox (format "/home/jao/var/mail/%s" + (match-string 1 gnus-newsgroup-name)))) + (jao-maildir-update-info-string mbox))))) + (with-eval-after-load "gnus-sum" + (add-hook 'gnus-exit-group-hook #'jao-maildir--ensure-counts))) + + (jao-maildir-setup jao-maildir-maildirs jao-maildir-tracked-maildirs -20) + #+end_src +* bbdb + #+begin_src emacs-lisp + (use-package bbdb + :ensure t + :init (setq bbdb-complete-name-allow-cycling t + bbdb-completion-display-record nil + bbdb-gui t + bbdb-message-all-addresses t + bbdb-complete-mail-allow-cycling t + bbdb-north-american-phone-numbers-p nil + bbdb-add-aka t + bbdb-add-name 2 + bbdb-message-all-addresses t + bbdb-mua-pop-up t ;; 'horiz + bbdb-mua-pop-up-window-size 0.3 + bbdb-layout 'multi-line + bbdb-mua-update-interactive-p '(query . create) + bbdb-mua-auto-update-p 'bbdb-select-message + bbdb-user-mail-address-re jao-mails-regexp + bbdb-auto-notes-ignore-headers + `(("From" . ,jao-mails-regexp) + ("From" . ".*@.*github\.com.*") + ("To" . ".*@.*github\.com.*") + ("Reply-to" . ".*") + ("References" . ".*")) + bbdb-auto-notes-ignore-messages + `(("To" . ".*@.*github\\.com.*") + ("From" . ".*@.*github\\.com.*") + ("From" . "info-list") + ("From" . "no-?reply\\|deploy") + ("X-Mailer" . "MailChimp")) + bbdb-accept-message-alist + `(("To" . ,jao-mails-regexp) + ("Cc" . ,jao-mails-regexp) + ("BCc" . ,jao-mails-regexp)) + bbdb-ignore-message-alist bbdb-auto-notes-ignore-messages) + :config + (add-hook 'message-setup-hook 'bbdb-mail-aliases) + ;; (add-hook 'bbdb-notice-mail-hook 'bbdb-auto-notes) + (add-hook 'bbdb-after-change-hook (lambda (arg) (bbdb-save))) + (require 'bbdb-anniv) ;; BBDB 3.x this gets birthdays in org agenda + ;; and diary - clever stuff + (add-hook 'diary-list-entries-hook 'bbdb-anniv-diary-entries) + (eval-after-load "gnus-sum" + '(progn + (define-key gnus-summary-mode-map ":" 'bbdb-mua-annotate-sender) + (define-key gnus-summary-mode-map ";" 'bbdb-mua-annotate-recipients)))) + + (require 'bbdb) + (bbdb-initialize 'gnus 'message 'pgp 'mail) + (bbdb-mua-auto-update-init 'gnus) + (setq bbdb-file (expand-file-name "bbdb" gnus-home-directory)) + #+end_src @@ -25,6 +25,7 @@ (defun jao-eww-html-renderer (handle) (let ((shr-use-colors nil) (shr-use-fonts nil) + (mm-html-blocked-images nil) (fill-column (min (window-width) 110))) (mm-shr handle))) (setq mm-text-html-renderer 'jao-eww-html-renderer) @@ -12,6 +12,28 @@ (defvar jao-gnus-use-nnml nil) (defvar jao-gnus-use-maildirs nil) #+end_src +* Startup and kill + #+begin_src emacs-lisp + ;;;;; close gnus when closing emacs, but ask when exiting + (setq gnus-interactive-exit t) + + (defun jao-gnus-started-hook () + (add-hook 'before-kill-emacs-hook 'gnus-group-exit)) + + (add-hook 'gnus-started-hook 'jao-gnus-started-hook) + + (defun jao-gnus-after-exiting-hook () + (remove-hook 'before-kill-emacs-hook 'gnus-group-exit)) + + (add-hook 'gnus-after-exiting-gnus-hook 'jao-gnus-after-exiting-hook) + + ;; define a wrapper around the save-buffers-kill-emacs + ;; to run the new hook before: + (defadvice save-buffers-kill-emacs + (before my-save-buffers-kill-emacs activate) + "Install hook when emacs exits before emacs asks to save this and that." + (run-hooks 'before-kill-emacs-hook)) + #+end_src * Looks *** Verbosity #+begin_src emacs-lisp @@ -1494,461 +1494,6 @@ ;; for M-x biblio-lookup ;; (use-package biblio :ensure t) #+END_SRC -* Email -*** message mode -***** Customization - #+begin_src emacs-lisp - (defvar jao-mails "") - (defvar jao-mails-regexp) - (setq jao-mails-regexp (regexp-opt jao-mails)) - - (require 'message) - (setq message-send-mail-function 'message-send-mail-with-sendmail - message-sendmail-envelope-from 'header - message-sendmail-f-is-evil nil) - (setq imap-store-password t) - (setq password-cache-expiry nil) - (setq message-generate-headers-first t) - (setq message-forward-before-signature nil) - (setq message-alternative-emails jao-mails-regexp) - (setq message-dont-reply-to-names - (format "%s\\|%s" jao-mails-regexp (regexp-opt '("noreply@" "@noreply" - "no-reply@" "@no-reply" - "notifications@github")))) - (setq message-citation-line-format "On %a, %b %d %Y, %N wrote:\n") - (setq message-citation-line-function 'message-insert-formatted-citation-line) - - (setq message-user-fqdn "gnus.jao.io") - - ;; writing messages - (setq message-kill-buffer-on-exit t) - (setq message-max-buffers 5) - (setq message-insert-signature t) - (setq message-from-style 'angles - user-mail-address (car jao-mails) - mail-host-address system-name - message-syntax-checks '((sender . disabled)) - message-default-headers - (concat - "X-Attribution: jao\n" - "X-Clacks-Overhead: GNU Terry Pratchett\n" - "X-URL: <http://jao.io/>\n") - message-hidden-headers - '("^References:" "^Face:" "^X-Face:" "^X-Draft-From:") - message-make-forward-subject-function 'message-forward-subject-fwd) - - (setq message-expand-name-standard-ui t) - #+end_src -***** Encryption - #+BEGIN_SRC emacs-lisp - (require 'gnutls) - ;; avoiding bogus warning - (setq gnutls-min-prime-bits nil) - (setq gnus-buttonized-mime-types - '("multipart/encrypted" "multipart/signed" "multipart/alternative")) - - (setq mm-verify-option 'always) - (setq mm-decrypt-option 'always) - - (setq mm-sign-option 'guided) - (setq mm-encrypt-option 'guided) - - (setq mml-secure-passphrase-cache-expiry (* 3600 24) - password-cache-expiry (* 3600 24)) - - (setq smime-CA-directory "/etc/ssl/certs/" - smime-certificate-directory"/home/jao/.emacs.d/gnus/Mail/certs/") - - ;; Tells Gnus to inline the part - (eval-after-load "mm-decode" - '(add-to-list 'mm-inlined-types "application/pgp$")) - ;; Tells Gnus how to display the part when it is requested - (eval-after-load "mm-decode" - '(add-to-list 'mm-inline-media-tests '("application/pgp$" - mm-inline-text identity))) - ;; Tell Gnus not to wait for a request, just display the thing - ;; straight away. - (eval-after-load "mm-decode" - '(add-to-list 'mm-automatic-display "application/pgp$")) - ;; But don't display the signatures, please. - (eval-after-load "mm-decode" - (quote (setq mm-automatic-display (remove "application/pgp-signature" - mm-automatic-display)))) - - ;; decide whether to encrypt or just sign outgoing messages - (defvar jao-message-try-sign nil) - (defun jao-message-maybe-sign () - (when (and jao-message-try-sign (y-or-n-p "Sign message? ")) - (if (y-or-n-p "Encrypt message? ") - (let ((recipient (message-fetch-field "To"))) - (if (or (pgg-lookup-key recipient) - (and (y-or-n-p (format "Fetch %s's key? " recipient)) - (pgg-fetch-key pgg-default-keyserver-address - recipient))) - (mml-secure-message-encrypt-pgp) - (mml-secure-message-sign-pgp))) - (mml-secure-message-sign-pgp)))) - - ;; for ma gnus - (eval-after-load "rfc2047" - '(add-to-list 'rfc2047-header-encoding-alist - '("User-Agent" . address-mime))) - - ;; (define-key message-mode-map [f7] 'mml-secure-message-sign-pgp) - (define-key message-mode-map [f8] 'mml-secure-message-encrypt-pgp) - #+END_SRC -***** Attach image to message - Use ~C-c C-p~ in message-mode. -***** Check attachment - #+BEGIN_SRC emacs-lisp - (defvar jao-message-attachment-regexp "\\([Ww]e send\\|[Ii] send\\|attach\\)") - (defun jao-message-check-attachment () - "Check if there is an attachment in the message if I claim it." - (save-excursion - (message-goto-body) - (when (search-forward-regexp jao-message-attachment-regexp nil t nil) - (message-goto-body) - (unless (or (search-forward "<#part" nil t nil) - (message-y-or-n-p - "No attachment. Send the message? " nil nil)) - (error "No message sent"))))) - #+END_SRC -***** Check Gcc - #+BEGIN_SRC emacs-lisp - (defun jao-message-check-gcc () - "Ask whether to keep a copy of message." - (save-excursion - (save-restriction - (message-narrow-to-headers) - (when (and (message-fetch-field "Gcc") - (not (y-or-n-p "Archive? "))) - (message-remove-header "Gcc"))))) - - (defun jao-message-toggle-gcc () - "Insert or remove the \"Gcc\" header." - (interactive) - (save-excursion - (save-restriction - (message-narrow-to-headers) - (if (message-fetch-field "Gcc") - (message-remove-header "Gcc") - (gnus-inews-insert-gcc))))) - - ;; (define-key message-mode-map [(f6)] 'jao-message-toggle-gcc) - #+END_SRC -***** Check recipient - #+begin_src emacs-lisp - (defun jao-message-check-recipient () - (save-excursion - (save-restriction - (message-narrow-to-headers) - (when-let ((to (message-fetch-field "To"))) - (when (string-match-p jao-mails-regexp to) - (unless (y-or-n-p "Message is addressed to yourself. Continue?") - (error "Message not sent"))))))) - #+end_src -***** Randomsig - #+BEGIN_SRC emacs-lisp - (when (require 'randomsig nil t) - (define-key message-mode-map (kbd "C-c s") 'randomsig-replace-sig) - (define-key message-mode-map (kbd "C-c S") 'randomsig-select-sig) - (eval-after-load "gnus-sum" - '(define-key gnus-summary-save-map "-" 'gnus/randomsig-summary-read-sig)) - (setq randomsig-dir (expand-file-name "~/etc/config/emacs")) - (setq randomsig-files '("signatures.txt")) - ;; or (setq randomsig-files (randomsig-search-sigfiles)) - ;; or (setq randomsig-files 'randomsig-search-sigfiles) - (setq message-signature 'randomsig-signature) - (setq randomsig-delimiter-pattern "^%$" - randomsig-delimiter "%")) - #+END_SRC -***** Send mail hooks - #+BEGIN_SRC emacs-lisp - (dolist (h '(jao-message-check-gcc - jao-message-check-attachment - jao-message-check-recipient - jao-message-maybe-sign)) - (add-hook 'message-send-hook h)) - #+END_SRC -*** directories - #+BEGIN_SRC emacs-lisp - (setq gnus-home-directory "~/.emacs.d/gnus" - gnus-directory gnus-home-directory) - - (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-auto-save-directory (jao-gnus-dir "Mail/drafts/") - 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")) - #+END_SRC -*** sendmail/smtp - #+BEGIN_SRC emacs-lisp - (require 'smtpmail) - - (defun jao-sendmail-gmail () - (setq smtpmail-auth-supported '(login cram-md5 plain)) - (setq smtpmail-smtp-server "smtp.gmail.com") - (setq smtpmail-smtp-service 587)) - - (defun jao-sendmail-local () - (setq smtpmail-auth-supported nil) ;; (cram-md5 plain login) - (setq smtpmail-smtp-server "127.0.0.1") - (setq smtpmail-smtp-service 25)) - - ;; (jao-sendmail-gmail) - (jao-sendmail-local) - #+END_SRC -*** Gnus - The core of Gnus's configuration is tangled to the usual ~gnus.el~ - from [[./gnus.org][gnus.org]]. - #+BEGIN_SRC emacs-lisp - (defalias 'jao-open-gnus-frame 'jao-afio--goto-gnus) - - (setq gnus-init-file (jao-maybe-tangle "gnus")) - - ;;;;; close gnus when closing emacs, but ask when exiting - (setq gnus-interactive-exit t) - - (defun jao-gnus-started-hook () - "use that hook for its purpose " - (add-hook 'before-kill-emacs-hook 'gnus-group-exit)) - - (add-hook 'gnus-started-hook 'jao-gnus-started-hook) - - (defun jao-gnus-after-exiting-hook () - "how about removing this hook when exiting gnus in the conventional way?" - (remove-hook 'before-kill-emacs-hook 'gnus-group-exit)) - - (add-hook 'gnus-after-exiting-gnus-hook 'jao-gnus-after-exiting-hook) - - ;; define a wrapper around the save-buffers-kill-emacs - ;; to run the new hook before: - (defadvice save-buffers-kill-emacs - (before my-save-buffers-kill-emacs activate) - "Install hook when emacs exits before emacs asks to save this and that." - (run-hooks 'before-kill-emacs-hook)) - #+END_SRC -*** mbox listing - #+begin_src emacs-lisp - (defun jao-list-mailboxes (base) - (let ((dir (expand-file-name base "~/var/mail"))) - (cl-remove-if (lambda (x) - (member x '("." ".." "sent" "inbox" "trash"))) - (directory-files dir)))) - #+end_src -*** notmuch -***** Package configuration - #+BEGIN_SRC emacs-lisp - (defun jao-notmuch--mboxes-search (box) - (mapcar (lambda (m) - `(:name ,m - :search-type tree - :query ,(format "folder:%s/%s and tag:unread" - box m))) - (jao-list-mailboxes box))) - (when (file-exists-p "~/var/mail") - (use-package notmuch - :ensure t - :init - (setq notmuch-fcc-dirs '(("jao@bigml.com" . "bigml/sent") - (".*" . "jao/sent")) - notmuch-tagging-keys - '(("a" notmuch-archive-tags "Archive") - ("u" notmuch-show-mark-read-tags "Mark read") - ("f" ("+flagged") "Flag") - ("s" ("+spam" "-inbox") "Mark as spam") - ("d" ("+deleted" "-inbox") "Delete")) - notmuch-saved-searches - `((:name "jao" :key "j" - :query "folder:jao/inbox" - :search-type tree - :count-query "folder:jao/inbox and tag:unread") - (:name "bigml" :key "b" - :count-query "folder:bigml/inbox and tag:unread" - :search-type tree - :query "folder:bigml/inbox") - ,@(jao-notmuch--mboxes-search "jao") - ,@(jao-notmuch--mboxes-search "bigml") - ,@(jao-notmuch--mboxes-search "feeds") - (:name "unread" :query "tag:unread" :key "u" :search-type tree) - (:name "sent" :query "tag:sent" :key "t" :search-type tree) - (:name "drafts" :query "tag:draft" :key "d" :search-type tree) - (:name "all mail" :query "*" :count-query "tag:unread" :key "a" - :search-type tree)) - notmuch-hello-sections - '(notmuch-hello-insert-header - notmuch-hello-insert-saved-searches - notmuch-hello-insert-alltags - notmuch-hello-insert-footer)) - :bind (:map notmuch-show-mode-map - ("C-c C-c" . jao-notmuch-goto-message-in-gnus)))) - #+END_SRC -***** notmuch -> gnus - #+begin_src emacs-lisp - (defun jao-notmuch-goto-message-in-gnus () - "Open a summary buffer containing the current notmuch article." - (interactive) - (let ((group (jao-maildir-file-to-group (notmuch-show-get-filename))) - (message-id (replace-regexp-in-string "^id:" - "" - (notmuch-show-get-message-id)))) - (if (and group message-id) - (org-gnus-follow-link group message-id) - (message "Couldn't get relevant infos for switching to Gnus.")))) - #+end_src -*** visual message fill column - #+begin_src emacs-lisp - (use-package visual-fill-column - :ensure t - :init - (setq-default fringes-outside-margins nil - visual-fill-column-width 80 - visual-fill-column-fringes-outside-margins nil) - (setq gnus-treat-fill-long-lines nil) - :config - (setq split-window-preferred-function - #'visual-fill-column-split-window-sensibly) - :bind ((:map ctl-x-map ("M-f" . visual-fill-column-mode)))) - - ;; (add-hook 'gnus-article-mode-hook #'visual-line-mode) - ;; (add-hook 'gnus-article-mode-hook #'visual-fill-column-mode) - - ;; Name may be misleading, it does not set `fill-column' (which - ;; is still used by M-q) in `message-mode', but enables - ;; auto-filling on a given column. - ;; (setq message-fill-column nil) - ;; FIXME: There is no proper way to make fill commands to unfill. - ;; (add-hook 'message-mode-hook - ;; (lambda () - ;; (setq-local fill-column most-positive-fixnum))) - ;; (when-require 'visual-fill-column - ;; (add-hook 'message-mode-hook #'visual-fill-column-mode)) - #+end_src -*** mail this buffer - #+BEGIN_SRC emacs-lisp - (defun jao-mail-this-file () - (interactive) - (let ((file (buffer-file-name))) - (compose-mail) - (save-excursion - (message-goto-subject) - (insert (file-name-nondirectory file)) - (message-goto-body) - (newline 2) - (mml-attach-file file (mm-default-file-encoding file))))) - #+END_SRC -*** mailcap - #+BEGIN_SRC emacs-lisp - (require 'mailcap) - - (add-to-list 'mailcap-mime-extensions '(".JPEG" . "image/jpeg")) - (add-to-list 'mailcap-mime-extensions '(".JPG" . "image/jpeg")) - - (let* ((apps (cdr (assoc "application" mailcap-mime-data))) - (apps (cl-remove-if (lambda (x) (string= (car x) "pdf")) apps))) - (setcdr (assoc "application" mailcap-mime-data) apps) - (mailcap-parse-mailcaps nil t)) - #+END_SRC -*** frm - #+begin_src emacs-lisp - (use-package jao-frm - :init (setq jao-frm-mail-command 'jao-open-gnus-frame)) - - (defun jao-frm--formatter (mbox n) - (apply #'format "%s/%s: %s" `(,@(last (split-string mbox "/") 2) ,n))) - - (defun jao-frm--show () - (interactive) - (jao-frm-show-mail-numbers #'jao-frm--formatter)) - - (global-set-key [(f12)] 'jao-frm--show) - (global-set-key [(f8)] 'jao-frm) - - #+end_src -*** maildirs - #+begin_src emacs-lisp - (defvar jao-maildir-maildirs nil) - (defvar jao-maildir-tracked-maildirs nil) - (use-package jao-maildir - :config - (defun jao-maildir--ensure-counts () - (when gnus-newsgroup-name - (when (string-match "^nnimap.*:\\(.+\\)" gnus-newsgroup-name) - (let ((mbox (format "/home/jao/var/mail/%s" - (match-string 1 gnus-newsgroup-name)))) - (jao-maildir-update-info-string mbox))))) - (with-eval-after-load "gnus-sum" - (add-hook 'gnus-exit-group-hook #'jao-maildir--ensure-counts))) - - (jao-maildir-setup jao-maildir-maildirs jao-maildir-tracked-maildirs -20) - #+end_src -*** BBDB - #+BEGIN_SRC emacs-lisp - (use-package bbdb - :ensure t - :init (setq bbdb-complete-name-allow-cycling t - bbdb-completion-display-record nil - bbdb-gui t - bbdb-message-all-addresses t - bbdb-complete-mail-allow-cycling t - bbdb-north-american-phone-numbers-p nil - bbdb-add-aka t - bbdb-add-name 2 - bbdb-message-all-addresses t - bbdb-mua-pop-up t ;; 'horiz - bbdb-mua-pop-up-window-size 0.3 - bbdb-layout 'multi-line - bbdb-mua-update-interactive-p '(query . create) - bbdb-mua-auto-update-p 'bbdb-select-message - bbdb-user-mail-address-re jao-mails-regexp - bbdb-auto-notes-ignore-headers - `(("From" . ,jao-mails-regexp) - ("From" . ".*@.*github\.com.*") - ("To" . ".*@.*github\.com.*") - ("Reply-to" . ".*") - ("References" . ".*")) - bbdb-auto-notes-ignore-messages - `(("To" . ".*@.*github\\.com.*") - ("From" . ".*@.*github\\.com.*") - ("From" . "info-list") - ("From" . "no-?reply\\|deploy") - ("X-Mailer" . "MailChimp")) - bbdb-accept-message-alist - `(("To" . ,jao-mails-regexp) - ("Cc" . ,jao-mails-regexp) - ("BCc" . ,jao-mails-regexp)) - bbdb-ignore-message-alist bbdb-auto-notes-ignore-messages) - :config - (add-hook 'message-setup-hook 'bbdb-mail-aliases) - ;; (add-hook 'bbdb-notice-mail-hook 'bbdb-auto-notes) - (add-hook 'bbdb-after-change-hook (lambda (arg) (bbdb-save))) - (require 'bbdb-anniv) ;; BBDB 3.x this gets birthdays in org agenda - ;; and diary - clever stuff - (add-hook 'diary-list-entries-hook 'bbdb-anniv-diary-entries) - (eval-after-load "gnus-sum" - '(progn - (define-key gnus-summary-mode-map ":" 'bbdb-mua-annotate-sender) - (define-key gnus-summary-mode-map ";" 'bbdb-mua-annotate-recipients)))) - - (require 'bbdb) - (bbdb-initialize 'gnus 'message 'pgp 'mail) - (bbdb-mua-auto-update-init 'gnus) - (setq bbdb-file (expand-file-name "bbdb" gnus-home-directory)) - - #+END_SRC * Browsing *** Variables #+begin_src emacs-lisp @@ -2169,6 +1714,12 @@ (jao-load-org "eww") ;; (jao-load-org "w3m") #+end_src +* Email + #+begin_src emacs-lisp + (setq jao-afio-mail-function 'notmuch + jao-afio-notmuch-in-web t) + (jao-load-org "email") + #+end_src * PDFs *** doc-view #+begin_src emacs-lisp diff --git a/lib/eos/jao-afio.el b/lib/eos/jao-afio.el index 1410b80..e6f1e72 100644 --- a/lib/eos/jao-afio.el +++ b/lib/eos/jao-afio.el @@ -20,6 +20,12 @@ ;;; Code: +(defvar jao-open-doc-fun 'find-file) +(defvar jao-afio-mail-function 'gnus) +(defvar jao-afio-use-w3m nil) +(defvar jao-afio-notmuch-in-web t) +(defvar jao-afio-switch-hook nil) + (defvar jao-afio--configs '(?c ?w ?g ?p ?s)) (defvar jao-afio--current-config (car jao-afio--configs)) (defvar jao-afio--locker nil) @@ -64,9 +70,6 @@ (jao-afio--goto-frame next))) ;;;###autoload -(defvar jao-open-doc-fun 'find-file) - -;;;###autoload (defun jao-afio-open-pdf-session () (interactive) (dolist (doc (jao-doc-view-session)) @@ -86,10 +89,6 @@ (when (and (jao-doc-view-session) (y-or-n-p "Load saved session? ")) (jao-afio-open-pdf-session))))) -(defvar jao-afio-mail-function 'gnus) -(defvar jao-afio-use-w3m nil) -(defvar jao-afio-notmuch-in-web t) - (declare w3m "w3m") (declare w3m-alive-p "w3m") (declare w3m-previous-buffer "w3m") @@ -128,9 +127,11 @@ (interactive) (if (or (null jao-afio-mail-function) (eq 'gnus jao-afio-mail-function)) (jao-afio-open-gnus) - (delete-other-windows) + (jao-trisect) + (other-window 2) + (delete-window) + (other-window 1) (funcall jao-afio-mail-function) - (jao-bisect) (other-window 1) (find-file (expand-file-name "inbox.org" org-directory)) (split-window-below (/ (window-height) 3)) @@ -141,8 +142,6 @@ (switch-to-buffer "*Calendar*") (other-window 1))) -(defvar jao-afio-switch-hook nil) - (defun jao-afio--goto-frame (next &optional reset) (when (or reset (not (eq next jao-afio--current-config))) (let ((next-cfg (when (not reset) (get-register next)))) diff --git a/lib/themes/jao-themes.el b/lib/themes/jao-themes.el index 6e55cc6..fbb4827 100644 --- a/lib/themes/jao-themes.el +++ b/lib/themes/jao-themes.el @@ -861,10 +861,24 @@ (nrepl-output-face (p f02)) (nrepl-prompt-face (p f00)) (nrepl-result-face nil) + (notmuch-crypto-decryption (~ success)) + (notmuch-crypto-part-header (p f11)) + (notmuch-crypto-signature-bad (p error) ul) + (notmuch-crypto-signature-good (~ success)) + (notmuch-crypto-signature-unknown (p error) ul) + (notmuch-crypto-signature-good-key (~ success)) + (notmuch-hello-logo-background nil) + (notmuch-message-summary-face (p f00)) (notmuch-search-count (p f00)) (notmuch-search-date (p f01)) + (notmuch-search-flagged-face (p warn)) (notmuch-search-matching-authors (p f02)) - (notmuch-search-subject nil)) + (notmuch-search-subject nil) + (notmuch-tag-face (p f00) it) + (notmuch-tag-unread (p warn)) + (notmuch-tree-match-author-face (p f01)) + (notmuch-tree-match-tag-face ul) + (notmuch-wash-cited-text (~ gnus-cite-1))) `((orderless-match-face-0 ul) (orderless-match-face-1 ul) (orderless-match-face-2 ul) @@ -65,6 +65,7 @@ - [[./completion.org][completion.org]]: completion setup using company, consult and friends. - [[./org.org][org.org]] org mode configuration. - [[./blog.org][blog.org]]: blogging using org-static-blog. +- [[./email.org][email.org]]: email handling outside of gnus. - [[./gnus.org][gnus.org]]: tangled to gnus.el automatically by init.org, so that it's ready for loading by Gnus. - [[./w3m.org][w3m.org]]: browsing with emacs-w3m. |