;;; mail.el --- My general mail configurations -*- lexical-binding: t; -*- ;; Copyright (C) 2022 Sévère Durand ;; Author: Sévère Durand ;; Keywords: convenience, mail ;; 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: ;; This file contains my general configurations related to mails. ;;; Code: ;;; Some settings ;;;; Setting my mail address (setq user-full-name "Jean Sévère Durand") (setq user-mail-address "durand@jsdurand.xyz") ;;;; Confirm to send (setq message-confirm-send t) ;;;; Sending settings (setq message-send-mail-function 'smtpmail-send-it) (setq smtpmail-stream-type 'starttls) (setq smtpmail-default-smtp-server "smtp.gmail.com") (setq smtpmail-smtp-server "smtp.gmail.com") (setq smtpmail-smtp-service 587) ;;; message mode (require 'message) (defun durand-set-fill-column () "Set the fill-column to an appropriate value." (setq fill-column 70)) (add-hook 'message-mode-hook #'durand-set-fill-column) ;;; Quote a message (defvar durand-quote-history nil "The history of names whom I have quoted.") (autoload 'durand-take "common") (defun durand-quote-message (&optional arg) "Quote the current message body, and add some meta-data. Actually this only saves the quote text in the `kill-ring', and does not modify the buffer directly. If region is activated, only quote the text in the region. If ARG is (list 4), query for the time and the name to quote. If ARG is non-nil, and not equal to (list 4), omit the line about the time and the one who says the quote; only query the author and produce the quoted message." (interactive (list current-prefix-arg) gnus-summary-mode gnus-article-mode message-mode) (cond ((apply #'provided-mode-derived-p major-mode (command-modes #'durand-quote-message))) ((user-error (concat "The command only works in the following modes: " (mapconcat (lambda (obj) (format "%S" obj)) (command-modes #'durand-quote-message) " "))))) (cond ((derived-mode-p 'gnus-summary-mode) (set-buffer gnus-article-buffer))) (let* ((date (cond ((and (listp arg) (null (cdr arg)) (eq (car arg) 4)) (read-string "Time of the quote: ")) (arg nil) ((save-match-data (save-restriction (mail-narrow-to-head) (mail-fetch-field "Date")))))) (date (cond (date (format-time-string "%a, %e %b %Y %H:%M:%S %z" (let* ((time (parse-time-string date)) (first-six (mapcar (lambda (element) (or element 0)) (durand-take 6 time))) (remain (nthcdr 6 time))) (encode-time (append first-six remain))) (current-time-zone))))) (author (cond (arg (read-string "Author of the quote: " nil 'durand-quote-history durand-quote-history t)) ((save-match-data (save-restriction (mail-narrow-to-head) (mail-fetch-field "From")))))) (author-name (save-match-data (cond ((string-match (rx "\"" (group (1+ anychar)) "\" <") author) (car (split-string (match-string 1 author)))) ((string-match (rx (group (1+ (not (any space "<")))) " <") author) (match-string 1 author)) ((string-match (rx "<" (group (1+ (not (any "<>")))) ">") author) (match-string 1 author)) (author)))) (body (cond ((use-region-p) (buffer-substring-no-properties (region-beginning) (region-end))) ((save-match-data (save-restriction (widen) (article-goto-body) (buffer-substring-no-properties (point) (point-max)))))))) (kill-new (concat (cond (date (format ">>>>> Le %s, %s a dit:\n\n" date author))) (mapconcat (lambda (line) (cond ;; paranoia ((not (stringp line)) (format "%S" line)) ((string-match-p (rx bos (zero-or-more space) (or (seq (1+ (not (any ">"))) "> ") (1+ (literal ">")) eos)) line) line) ((concat " " author-name "> " line)))) (split-string body (string #xa)) (string #xa)))) (message "Quoted the mail. To use it just yank it."))) (define-key message-mode-map (vector 3 ?q) #'durand-quote-message) (define-key gnus-summary-mode-map (vector 3 ?q) #'durand-quote-message) (define-key gnus-article-mode-map (vector 3 ?q) #'durand-quote-message) ;;; Fix the quotation style (defun durand-fix-quotation (&optional just-use-durand) "Fix the quotation style. If JUST-USE-DURAND is non-nil, just use \"Durand\" as the person." (interactive "P") (save-match-data (save-excursion (let* ((beg (region-beginning)) (end (region-end)) (end (progn (goto-char end) (point-marker))) (person (cond (just-use-durand "Durand") ((read-string "Name: "))))) (goto-char beg) (while (< (point) (marker-position end)) (cond ((re-search-forward (rx-to-string '(seq ">>" eol)) (marker-position end) t) (replace-match "" nil nil nil 0))) (forward-line 1)) (goto-char beg) (while (< (point) (marker-position end)) (cond ((looking-at (rx-to-string '(seq bol (zero-or-more (in space)) (group (one-or-more ">>" (one-or-more (in space))) (group (one-or-more (not (in ">\n")))) (one-or-more ">>" (one-or-more (in space))))) t)) (let ((middle-text (match-string 2))) (replace-match (format "%s> %s" person middle-text) t t nil 1))) ((looking-at (rx-to-string '(seq bol (one-or-more (in space)) (group (one-or-more ">>" (one-or-more (in space))))) t)) (replace-match (format "%s> " person) t t nil 1))) (forward-line 1)) (fill-region beg (marker-position end)) (message "Processing...Done"))))) (define-key message-mode-map (vector 3 ?f) #'durand-fix-quotation) (define-key gnus-summary-mode-map (vector 3 ?f) #'durand-fix-quotation) (define-key gnus-article-mode-map (vector 3 ?f) #'durand-fix-quotation) ;;; update mails without mu4e (defun durand-mail-process-output (output) "Normalize the OUTPUT emitted by mbsync." (let ((splitted (split-string output (rx-to-string (list 'any ? ?\n ?\r) t) t))) (or (car (last splitted)) ""))) (defun durand-mail-update-filter (process string) "Insert the ouput STRING into the buffer of PROCESS for updating \ mails." (let ((buffer (process-buffer process)) (output (durand-mail-process-output string))) (cond ((buffer-live-p buffer) (display-buffer buffer (list (list #'display-buffer-in-side-window) (cons 'side 'bottom) (cons 'window-height #'durand-fit-window-to-buffer-with-max))) (with-current-buffer buffer (cond ((and (stringp output) (not (string= output ""))) (delete-region (point-min) (point-max)) (insert output) (set-marker (process-mark process) (point-max) buffer)))))))) (defvar durand-mail-update-hook nil "The hook run after the process that updates mails is finished.") (defun durand-mail-update-sentinel (process status) "Handle STATUS changes of the PROCESS for updating mails. This funciton runs the hook `durand-mail-update-hook' after the PROCESS is finished." (cond ((string= status "finished\n") (run-hooks 'durand-mail-update-hook) (let ((buffer (process-buffer process))) (cond ((buffer-live-p buffer) (quit-window nil (get-buffer-window buffer)))))))) (defvar durand-mail-update-command (list "mbsync" "mymail-inbox" "mymail-sent" "gmail-inbox" "gmail-sent") "Command to update mails.") (defvar durand-mail-update-buffer "*mail*" "Buffer to display progess of updating mails.") (autoload #'durand-internet-on "basic.el") (defun durand-mail-update (&optional foreground-p) "Update mails. If FOREGROUND-P is non-nil, also display the progress in a separate buffer. If FOREGROUND-P is `0', do not fetch mails, but just run `durand-mail-update-hook.'" (interactive (list (let ((arg current-prefix-arg)) (cond ((and (numberp arg) (= arg 0)) 0) ((null arg)))))) (let ((process-found (get-process "durand-mail-update")) (internet-on (durand-internet-on))) (cond ((and (numberp foreground-p) (= foreground-p 0)) (run-hooks 'durand-mail-update-hook)) ((and (not process-found) internet-on) (make-process :name "durand-mail-update" :command durand-mail-update-command :filter (cond (foreground-p #'durand-mail-update-filter)) :sentinel (cond (foreground-p #'durand-mail-update-sentinel)) :buffer (cond (foreground-p (let ((buffer (get-buffer-create durand-mail-update-buffer))) (buffer-disable-undo buffer) buffer))))) ((not (or process-found internet-on)) (message "There is no internet connection."))))) (defvar durand-mail-update-timer nil "A timer to automatically update mail in the background.") (defun durand-mail-cancel-timer () "Cancel `durand-mail-update-timer'." (cancel-timer durand-mail-update-timer)) (cond ((memq #'durand-mail-update (mapcar #'timer--function timer-list))) ((setq durand-mail-update-timer (run-with-timer 0 (* 60 30) #'durand-mail-update)))) (provide 'mail) ;;; mail.el ends here