From 47d7a42c7cfd8d8517a3c807b4e3f11b1246cc1f Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Sat, 26 Aug 2023 20:31:44 +0200 Subject: [PATCH] Refactor Emacs configuration a bit --- .emacs.d/init.el | 146 ++--------------------------------- .emacs.d/lisp/esy-capture.el | 56 -------------- .emacs.d/lisp/esy-comm.el | 139 +++++++++++++++++++++++++++++++++ .emacs.d/lisp/esy-o365.el | 65 ++++++++++++++++ 4 files changed, 210 insertions(+), 196 deletions(-) delete mode 100644 .emacs.d/lisp/esy-capture.el create mode 100644 .emacs.d/lisp/esy-comm.el create mode 100644 .emacs.d/lisp/esy-o365.el diff --git a/.emacs.d/init.el b/.emacs.d/init.el index 9c53923..ec7b12c 100644 --- a/.emacs.d/init.el +++ b/.emacs.d/init.el @@ -230,6 +230,8 @@ user-full-name "Eshel Yaron" ;; my email address user-mail-address "me@eshelyaron.com" + mail-user-agent 'gnus-user-agent + read-mail-command 'gnus ;; direct Custom definitions to some file and forget about it custom-file (expand-file-name "custom.el" user-emacs-directory) ;; silence native compilation warnings @@ -393,71 +395,10 @@ vc-follow-symlinks t ;; jump from .c to .h files with find-sibling-file find-sibling-rules '(("\\([^/]+\\)\\.c\\'" "\\1.h")) - message-alternative-emails (regexp-opt '("eshelshay.yaron@gmail.com" - "me@eshelyaron.com" - "eshel@swi-prolog.org" - "eshel.yaron@student.uva.nl")) - ;; set function for sending email - send-mail-function #'esy/smtp-send-it - message-send-mail-function #'esy/smtp-send-it - ;; set email user agent - mail-user-agent 'gnus-user-agent - read-mail-command 'gnus - ;; make Gnus not prompt as much - gnus-expert-user t - ;; make Gnus read the dribble file on startup without prompting - gnus-always-read-dribble-file t - ;; don't break pages in incoming email - gnus-break-pages nil - ;; don't show the Gnus startup screen - gnus-inhibit-startup-message t - ;; set Gnus backends - gnus-select-method '(nntp "news.gmane.io") - gnus-cite-parse-max-size nil - gnus-face-1 'bold - gnus-face-2 'gnus-cite-1 - gnus-face-3 'shadow - gnus-summary-line-format "%1{%U%R%z%}%B%2{%~(form (replace-regexp-in-string \" via .*\" \"\" (gnus-summary-from-or-to-or-newsgroups gnus-tmp-header gnus-tmp-from)))@%} %3{(%&user-date;, %k)%}:%* %s\n" - gnus-simplify-subject-functions '(gnus-simplify-subject-re) - gnus-treat-display-smileys nil - gnus-secondary-select-methods '((nnimap "gmail" - (nnimap-address "imap.gmail.com") - (nnimap-server-port "imaps") - (nnimap-stream ssl)) - (nnimap "me" - (nnimap-address "mail.eshelyaron.com") - (nnimap-server-port "imaps") - (nnimap-stream ssl)) - (nnimap "swi" - (nnimap-address "mail.swi-prolog.com") - (nnimap-server-port "imaps") - (nnimap-stream ssl)) - (nnimap "uva" - (nnimap-address "outlook.office365.com") - (nnimap-server-port "imaps") - (nnimap-stream ssl) - (nnimap-authenticator xoauth2))) - ;; simpler message - gnus-no-groups-message "No new articles" - ;; don't close other windows when opening Gnus - gnus-use-full-window nil - ;; make Gnus handle this content types - gnus-article-treat-types '("text/plain" - "text/x-verbatim" - "text/x-patch" - "text/html" - "text/calendar") - ;; capture calendar events from Gnus to my Org inbox file - gnus-icalendar-org-capture-file "~/org/inbox.org" - ;; put calendar events under the Calendar heading - gnus-icalendar-org-capture-headline '("Calendar") ;; use ISO format for calendar dates calendar-date-style 'iso ;; maintain legibility of rendered text in HTML mails shr-color-visible-luminance-min 75 - ;; my Mastodon settings - mastodon-instance-url "https://emacs.ch" - mastodon-active-user "eshel" ;; direct Magit to my Git checkouts directory magit-repository-directories '(("~/checkouts/" . 1)) ;; have Dired operations target another visible Dired buffer @@ -467,12 +408,6 @@ vterm-use-vterm-prompt-detection-method t ;; use MPV with EMMS emms-player-list '(emms-player-mpv) - ;; free-style numbering plan - bbdb-phone-style nil - ;; allow cycling through mail address completion candidate - bbdb-complete-mail-allow-cycling t - ;; don't pop up BBDB records after completing mail addresses - bbdb-completion-display-record nil eww-auto-rename-buffer 'title browse-url-browser-function #'eww-browse-url browse-url-generic-program "open" @@ -532,9 +467,7 @@ "Global Bindings:" "Function key map translations" "pixel-scroll-precision-mode" - "context-menu-mode")))) - report-emacs-bug-no-explanations t - submit-emacs-patch-display-help nil) + "context-menu-mode"))))) (setq-default indent-tabs-mode nil) @@ -602,9 +535,11 @@ (add-to-list 'load-path (expand-file-name "lisp/" user-emacs-directory)) (autoload 'some-button "some-button" nil t) -(autoload 'esy-capture "esy-capture" nil t) (autoload 'pdf-view-mode "pdf-view" nil t) +(with-eval-after-load 'gnus + (require 'esy-comm)) + ;;; Define custom commands (defvar-keymap transpose-lines-repeat-map @@ -1116,75 +1051,6 @@ as the initial input for completion, and return that directory." (add-to-list 'org-agenda-custom-commands '("h" "Holland TODOs" tags-todo "+holland"))) -;;; Outlook OAuth 2.0 setup - -(defvar esy/o365-token-last-refresh-time 0) -(defvar esy/o365-token-directory "~/checkouts/M365-IMAP/") - -(add-to-list 'auth-sources (expand-file-name ".authinfo" - esy/o365-token-directory)) - -(add-to-list 'savehist-additional-variables 'esy/o365-token-last-refresh-time) - -(defun esy/refresh-o365-token () - (interactive) - (files--ensure-directory esy/o365-token-directory) - (when-let ((default-directory esy/o365-token-directory) - (token (car (process-lines "python3" "refresh_token.py")))) - (with-temp-buffer - (insert "machine smtp.office365.com smtp-auth xoauth2 login eshel.yaron@student.uva.nl port 587 password \"" - token - "\"\nmachine outlook.office365.com login eshel.yaron@student.uva.nl port imaps password \"" - token - "\"\n") - (write-region (point-min) (point-max) ".authinfo")) - (auth-source-forget-all-cached))) - -(defun esy/maybe-refresh-o365-token () - (let ((now (float-time))) - (when (< (* 60 45) (- now esy/o365-token-last-refresh-time)) - (esy/refresh-o365-token) - (setq esy/o365-token-last-refresh-time now)))) - -;;; Configure email via `gnus' - -(with-eval-after-load 'gnus - (require 'gnus-icalendar) - (add-hook 'gnus-group-mode-hook #'gnus-topic-mode) - (add-hook 'gnus-before-resume-hook #'esy/maybe-refresh-o365-token) - (add-hook 'gnus-before-startup-hook #'esy/maybe-refresh-o365-token) - (gnus-icalendar-setup) - (gnus-icalendar-org-setup) - (bbdb-initialize 'gnus 'mail 'message)) - - -(defvar esy/smtp-accounts - '(("eshelshay.yaron@gmail.com" . "smtp.gmail.com") - ("eshel@swi-prolog.org" . "mail.swi-prolog.com") - ("me@eshelyaron.com" . "mail.eshelyaron.com") - ("eshel.yaron@student.uva.nl" . "smtp.office365.com"))) - -(defun esy/mail-from () - (without-restriction - (message-narrow-to-headers) - (when-let ((from (mail-fetch-field "from"))) - (cadr (mail-extract-address-components from))))) - -(defun esy/smtp-send-it () - (require 'smtpmail) - (if-let ((from (esy/mail-from)) - (server (alist-get from esy/smtp-accounts nil nil #'string=))) - (let ((smtpmail-smtp-user from) - (smtpmail-smtp-server server) - (smtpmail-smtp-service 587) - (smtpmail-stream-type 'starttls) - (smtpmail-servers-requiring-authorization ".*")) - (smtpmail-send-it)) - (user-error "Unrecognized FROM address, not sending"))) - -(with-eval-after-load 'message - (add-hook 'message-send-hook 'ispell-message)) - ;;; Configure `dired' (with-eval-after-load 'dired diff --git a/.emacs.d/lisp/esy-capture.el b/.emacs.d/lisp/esy-capture.el deleted file mode 100644 index 2259935..0000000 --- a/.emacs.d/lisp/esy-capture.el +++ /dev/null @@ -1,56 +0,0 @@ -;;; esy-capture.el --- capture a new post -*- lexical-binding: t; -*- - -;; Copyright (C) 2023 Eshel Yaron - -;; Author: Eshel Yaron -;; Keywords: convenience - -;; 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 . - -;;; Commentary: - -;; - -;;; Code: - -(defvar esy-capture-directory "~/checkouts/eshelyaron.com/draft") - -(defvar esy-capture-keywords '("emacs" - "prolog" - "language" - "politics" - "ai" - "lisp")) - -(defun esy-capture (title subtitle description keywords date) - (interactive (list (read-string "Title: ") - (read-string "Subtitle: ") - (read-string "Description: ") - (completing-read-multiple "Keywords: " esy-capture-keywords) - (format-time-string "%F"))) - (find-file (expand-file-name - (concat date - "-" - (downcase (string-join (string-split title (rx (+ (not alnum))) t) "-")) - ".org") - esy-capture-directory)) - (insert "#+TITLE: " title "\n" - "#+SUBTITLE: " subtitle "\n" - "#+DESCRIPTION: " description "\n" - "#+KEYWORDS: " (string-join keywords ",") "\n" - "#+DATE: " date "\n") - (set-buffer-modified-p nil)) - -(provide 'esy-capture) -;;; esy-capture.el ends here diff --git a/.emacs.d/lisp/esy-comm.el b/.emacs.d/lisp/esy-comm.el new file mode 100644 index 0000000..47a35b3 --- /dev/null +++ b/.emacs.d/lisp/esy-comm.el @@ -0,0 +1,139 @@ +;;; esy-comm.el --- My communication settings -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Eshel Yaron + +;; Author: Eshel Yaron +;; Keywords: comm + +;; 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 . + +;;; Commentary: + +;; + +;;; Code: + +(require 'esy-o365) +(require 'savehist) + +(defvar esy-comm-accounts + '(("me" + "me@eshelyaron.com" "mail.eshelyaron.com" "mail.eshelyaron.com") + ("gmail" + "eshelshay.yaron@gmail.com" "imap.gmail.com" "smtp.gmail.com") + ("swi" + "eshel@swi-prolog.org" "mail.swi-prolog.com" "mail.swi-prolog.com") + ("uva" + "eshel.yaron@student.uva.nl" "outlook.office365.com" "smtp.office365.com" + (nnimap-authenticator xoauth2)))) + +(defvar esy-comm-summary-line-format + (concat "%1{%U%R%z%}%B" + "%2{%~(form (replace-regexp-in-string \" via .*\" \"\" (gnus-summary-from-or-to-or-newsgroups gnus-tmp-header gnus-tmp-from)))@%} " + "%3{(%&user-date;, %k)%}:%* %s\n")) + +(defun esy-comm--mail-from () + (without-restriction + (message-narrow-to-headers) + (when-let ((from (mail-fetch-field "from"))) + (cadr (mail-extract-address-components from))))) + +(defun esy-comm-send-message () + (require 'smtpmail) + (if-let ((from (esy-comm--mail-from)) + (server (alist-get from + (mapcar (pcase-lambda + (`(,_ ,car ,_ ,cdr . ,_)) + (cons car cdr)) + esy-comm-accounts) + nil nil #'string=))) + (let ((smtpmail-smtp-user from) + (smtpmail-smtp-server server) + (smtpmail-smtp-service 587) + (smtpmail-stream-type 'starttls) + (smtpmail-servers-requiring-authorization ".*")) + (smtpmail-send-it)) + (user-error "Unrecognized FROM address, not sending"))) + +(setq + ;; sendmail.el + send-mail-function #'esy-comm-send-message + + ;; Message + message-send-mail-function #'esy-comm-send-message + message-alternative-emails (regexp-opt (mapcar #'cadr esy-comm-accounts)) + + ;; Gnus + gnus-expert-user t + gnus-always-read-dribble-file t + gnus-break-pages nil + gnus-inhibit-startup-message t + gnus-cite-parse-max-size nil + gnus-face-1 'bold + gnus-face-2 'gnus-cite-1 + gnus-face-3 'shadow + gnus-summary-line-format esy-comm-summary-line-format + gnus-simplify-subject-functions '(gnus-simplify-subject-re) + gnus-treat-display-smileys nil + gnus-select-method '(nntp "news.gmane.io") + gnus-secondary-select-methods (mapcar + (pcase-lambda + (`(,name ,_ ,address ,_ . ,tail)) + `(nnimap ,name + (nnimap-address ,address) + (nnimap-server-port "imaps") + (nnimap-stream ssl) + . + ,tail)) + esy-comm-accounts) + gnus-no-groups-message "No new articles" + gnus-use-full-window nil + gnus-article-treat-types '("text/plain" + "text/x-verbatim" + "text/x-patch" + "text/html" + "text/calendar") + gnus-icalendar-org-capture-file "~/org/inbox.org" + gnus-icalendar-org-capture-headline '("Calendar") + + ;; Mastodon + mastodon-instance-url "https://emacs.ch" + mastodon-active-user "eshel" + + ;; BBDB + bbdb-phone-style nil + bbdb-complete-mail-allow-cycling t + bbdb-completion-display-record nil + + ;; emacsbug.el + report-emacs-bug-no-explanations t + submit-emacs-patch-display-help nil) + +(with-eval-after-load 'gnus + (require 'gnus-icalendar) + (add-hook 'gnus-group-mode-hook #'gnus-topic-mode) + (esy-o365-setup 'gnus) + (gnus-icalendar-setup) + (gnus-icalendar-org-setup) + (bbdb-initialize 'gnus 'mail 'message)) + +(with-eval-after-load 'message + (add-hook 'message-send-hook 'ispell-message)) + +(with-eval-after-load 'savehist + (add-to-list 'savehist-additional-variables + 'esy-o365-token-refresh-last-time)) + +(provide 'esy-comm) +;;; esy-comm.el ends here diff --git a/.emacs.d/lisp/esy-o365.el b/.emacs.d/lisp/esy-o365.el new file mode 100644 index 0000000..c69986e --- /dev/null +++ b/.emacs.d/lisp/esy-o365.el @@ -0,0 +1,65 @@ +;;; esy-o365.el --- Settings for outlook 365 -*- lexical-binding: t; -*- + +;; Copyright (C) 2023 Eshel Yaron + +;; Author: Eshel Yaron +;; Keywords: comm + +;; 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 . + +;;; Commentary: + +;; + +;;; Code: + +(defvar esy-o365-token-directory "~/checkouts/M365-IMAP/") +(defvar esy-o365-token-refresh-last-time 0) +(defvar esy-o365-token-refresh-command '("python3" "refresh_token.py")) + +(defun esy-o365-refresh-token () + (interactive) + (files--ensure-directory esy-o365-token-directory) + (when-let ((default-directory esy-o365-token-directory) + (token (car (process-lines "python3" "refresh_token.py")))) + (with-temp-buffer + (insert "machine smtp.office365.com smtp-auth xoauth2 login eshel.yaron@student.uva.nl port 587 password \"" + token + "\"\nmachine outlook.office365.com login eshel.yaron@student.uva.nl port imaps password \"" + token + "\"\n") + (write-region (point-min) (point-max) ".authinfo")) + (auth-source-forget-all-cached))) + +(defun esy-o365-maybe-refresh-token () + (let ((now (float-time))) + (when (< (* 60 45) (- now esy-o365-token-refresh-last-time)) + (esy-o365-refresh-token) + (setq esy-o365-token-refresh-last-time now)))) + +;;;###autoload +(defun esy-o365-setup (mua) + (if (not (file-exists-p esy-o365-token-directory)) + (let ((m "`esy-o365-token-directory' does not exist")) + (if after-init-time (user-error m) (display-warning 'comm m))) + (require 'auth-source) + (add-to-list 'auth-sources + (expand-file-name ".authinfo" esy-o365-token-directory)) + (pcase mua + ('gnus + (dolist (hook '(gnus-before-startup-hook gnus-before-resume-hook)) + (add-hook hook #'esy-o365-maybe-refresh-token) ))))) + +(provide 'esy-o365) +;;; esy-o365.el ends here -- 2.39.2