diff --git a/README.org b/README.org index d3a666c..df941a9 100644 --- a/README.org +++ b/README.org @@ -7,19 +7,23 @@ #+LATEX_HEADER: \lstset{breaklines=true, breakatwhitespace=true, basicstyle=\ttfamily\footnotesize, columns=fullflexible} #+begin_src emacs-lisp :exports none :tangle no +;; This asks for a file which it uses to store the processed org +;; data with the includes into. It can be anything and can safely +;; be deleted afterwards. I think README.org is a good file to +;; pick if using a repository. (defun tangle-literate-config () - "Tangle all my literate configuration files." + "Tangle config files with proper include handling." (interactive) - (let ((org-confirm-babel-evaluate nil) - (files '("config.org" - "extra/feed.org" - "extra/email.org" - "extra/social.org"))) - (dolist (file files) - (when (file-exists-p file) - (message "Tangling %s..." (file-name-nondirectory file)) - (org-babel-tangle-file file))) - (message "All configuration files tangled!"))) + (let ((org-confirm-babel-evaluate nil)) + ;; Create a temporary buffer with expanded includes + (with-temp-buffer + (insert-file-contents "config.org") + (org-mode) + ;; This expands all #+INCLUDE directives + (org-export-expand-include-keyword) + ;; Now tangle from this buffer which has everything in one place + (org-babel-tangle nil "init.el")) + (message "Configuration tangled!"))) #+end_src #+BEGIN_SRC emacs-lisp :exports none @@ -480,11 +484,254 @@ on a system by system basis. # For now I'm not using headers here and letting them be defined in the org files # themselves. -#+INCLUDE: "extra/email.org" :minlevel 1 +#+PROPERTY: header-args :tangle email.el -#+INCLUDE: "extra/feed.org" :minlevel 1 +Incoming email is handled by [[https://notmuchmail.org][notmuch]]. Outgoing is via [[https://github.com/marlam/msmtp][msmtp]] -#+INCLUDE: "extra/social.org" :minlevel 1 +This is a pretty simple implementation without a lot of search queries, and +list handling. As I get more comfortable with using emacs as an email client +I'll try to get fancier. + +#+BEGIN_SRC emacs-lisp +;; Email/notmuch settings -*- lexical-binding: t; -*- +(use-package notmuch + :init + (autoload 'notmuch "notmuch" "notmuch mail" t) + :config + (define-key notmuch-show-mode-map "d" + (lambda () + "toggle deleted tag for message" + (interactive) + (if (member "deleted" (notmuch-show-get-tags)) + (notmuch-show-tag (list "-deleted")) + (notmuch-show-tag (list "+deleted"))))) + + (define-key notmuch-search-mode-map "d" + (lambda () + "toggle deleted tag for message" + (interactive) + (if (member "deleted" (notmuch-search-get-tags)) + (notmuch-search-tag (list "-deleted")) + (notmuch-search-tag (list "+deleted"))))) + + (define-key notmuch-tree-mode-map "d" + (lambda () + "toggle deleted tag for message" + (interactive) + (if (member "deleted" (notmuch-tree-get-tags)) + (notmuch-tree-tag (list "-deleted")) + (notmuch-tree-tag (list "+deleted")))))) + + +;; Saved searches +(setq notmuch-saved-searches '((:name "cheapbsd" + :query "tag:inbox and tag:cheapbsd" + :count-query "tag:inbox and tag:cheapbsd and tag:unread") + (:name "icloud" + :query "tag:inbox and tag:icloud" + :count-query "tag:inbox and tag:icloud and tag:unread") + (:name "sdf" + :query "tag:inbox and tag:sdf" + :count-query "tag:inbox and tag:sdf and tag:unread"))) + +(setq mml-secure-openpgp-sign-with-sender t) + +;; Sign messages by default. +(add-hook 'message-setup-hook 'mml-secure-sign-pgpmime) + +;; Use msmtp +(setq send-mail-function 'sendmail-send-it + sendmail-program "msmtp" + mail-specify-envelope-from t + message-sendmail-envelope-from 'header + mail-envelope-from 'header) + +(defun message-recipients () + "Return a list of all recipients in the message, looking at TO, CC and BCC. + +Each recipient is in the format of `mail-extract-address-components'." + (mapcan (lambda (header) + (let ((header-value (message-fetch-field header))) + (and + header-value + (mail-extract-address-components header-value t)))) + '("To" "Cc" "Bcc"))) + +(defun message-all-epg-keys-available-p () + "Return non-nil if the pgp keyring has a public key for each recipient." + (require 'epa) + (let ((context (epg-make-context epa-protocol))) + (catch 'break + (dolist (recipient (message-recipients)) + (let ((recipient-email (cadr recipient))) + (when (and recipient-email (not (epg-list-keys context recipient-email))) + (throw 'break nil)))) + t))) + +(defun message-sign-encrypt-if-all-keys-available () + "Add MML tag to encrypt message when there is a key for each recipient. + +Consider adding this function to `message-send-hook' to +systematically send encrypted emails when possible." + (when (message-all-epg-keys-available-p) + (mml-secure-message-sign-encrypt))) + +(add-hook 'message-send-hook #'message-sign-encrypt-if-all-keys-available) + +(setq notmuch-crypto-process-mime t) + +(defvar notmuch-hello-refresh-count 0) + +(defun notmuch-hello-refresh-status-message () + (let* ((new-count + (string-to-number + (car (process-lines notmuch-command "count")))) + (diff-count (- new-count notmuch-hello-refresh-count))) + (cond + ((= notmuch-hello-refresh-count 0) + (message "You have %s messages." + (notmuch-hello-nice-number new-count))) + ((> diff-count 0) + (message "You have %s more messages since last refresh." + (notmuch-hello-nice-number diff-count))) + ((< diff-count 0) + (message "You have %s fewer messages since last refresh." + (notmuch-hello-nice-number (- diff-count))))) + (setq notmuch-hello-refresh-count new-count))) + +(add-hook 'notmuch-hello-refresh-hook 'notmuch-hello-refresh-status-message) + +(defun color-inbox-if-unread () (interactive) + (save-excursion + (goto-char (point-min)) + (let ((cnt (car (process-lines "notmuch" "count" "tag:inbox and tag:unread")))) + (when (> (string-to-number cnt) 0) + (save-excursion + (when (search-forward "inbox" (point-max) t) + (let* ((overlays (overlays-in (match-beginning 0) (match-end 0))) + (overlay (car overlays))) + (when overlay + (overlay-put overlay 'face '((:inherit bold) (:foreground "green"))))))))))) +(add-hook 'notmuch-hello-refresh-hook 'color-inbox-if-unread) + +(custom-set-variables + ;; custom-set-variables was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. + '(notmuch-search-oldest-first nil)) +(custom-set-faces) + ;; custom-set-faces was added by Custom. + ;; If you edit it by hand, you could mess it up, so be careful. + ;; Your init file should contain only one such instance. + ;; If there is more than one, they won't work right. +#+end_src + +#+PROPERTY: header-args :tangle "feed.el" :noweb yes + +I get a lot of my news, and updates via Atom/RSS feeds. If I'm going to +browse them in emacs I use elfeed. + +#+name: header +#+begin_src emacs-lisp :exports none +;;; elfeed -- Just my elfeed config. -*- lexical-binding: t; -*- +;;; Commentary: +;;; Nothing yet. + +;;; Code: +#+end_src + +#+name: footer +#+begin_src emacs-lisp :exports none +(provide 'feed) +;;; feed.el ends here +#+end_src + +#+name: feed-src +#+begin_src emacs-lisp :exports code + +(use-package elfeed + :defer t + :bind + (("C-c F" . elfeed))) + +(use-package elfeed-org + :after (elfeed) + :config + (elfeed-org) + (setq rmh-elfeed-org-files (list "~/.emacs.d/extra/elfeed.org"))) + +(use-package elfeed-tube + :after (elfeed) + :config + (elfeed-tube-setup) + :bind (:map elfeed-show-mode-map + ("F" . elfeed-tube-fetch) + ([remap save-buffer] . elfeed-tube-save) + :map elfeed-search-mode-map + ("F" . elfeed-tube-fetch) + ([remap save-buffer] . elfeed-tube-save))) + +(use-package mpv + :ensure (:host github :repo "kljohann/mpv.el")) + +(use-package elfeed-tube-mpv + :after (elfeed-tube mpv) + :bind (:map elfeed-show-mode-map + ("C-c C-f" . elfeed-tube-mpv-follow-mode) + ("C-c C-w" . elfeed-tube-mpv-where))) + +(use-package elfeed-goodies + :after (elfeed) + :config + (elfeed-goodies/setup)) + +<