unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* Scheduling mails
@ 2024-04-02  0:18 ` João Pedro
  2024-04-02  1:05   ` Jose A Ortega Ruiz
  2024-04-04 16:38   ` Marc Fargas
  0 siblings, 2 replies; 8+ messages in thread
From: João Pedro @ 2024-04-02  0:18 UTC (permalink / raw)
  To: notmuch

Hi there,

This might not be the best mailing list to ask this, but I figured I
could at least be given some directions.

I am using notmuch in Emacs, with mbsync and msmtp (I actually use
smtpmail.el in Emacs, but it uses the `sendmail' command, which is
symlinked to msmtp) and the only thing I miss from other mail clients is
the ability to schedule a mail to be sent on a certain time. I do also
use `message-mode' to compose my emails, and it has `gnus-delay-article'
bound to C-c C-j, which at first sight seems to be exactly what I
need. The problem is, when calling it from =notmuch-message-mode= I get
a `wrong-type-argument' from the first line of `gnus-agent-queue-setup',
which tries to get a value from a hash table stored in a variable that
is initialized with nil. Gnus must initialize this variable with some
sort of hash table, but I could not emulate this behaviour to make this
scheduling work. Is it possible to use `message-mode''s scheduling
facility with notmuch?

Cheers,

-- 
João Pedro de A. Paula
IT bachelors at Universidade Federal do Rio Grande do Norte (UFRN)\r

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Scheduling mails
  2024-04-02  0:18 ` Scheduling mails João Pedro
@ 2024-04-02  1:05   ` Jose A Ortega Ruiz
  2024-04-02  4:34     ` João Pedro
  2024-04-04 16:38   ` Marc Fargas
  1 sibling, 1 reply; 8+ messages in thread
From: Jose A Ortega Ruiz @ 2024-04-02  1:05 UTC (permalink / raw)
  To: João Pedro, notmuch


Hi,

On Mon, Apr 01 2024, João Pedro wrote:

> I am using notmuch in Emacs, with mbsync and msmtp (I actually use
> smtpmail.el in Emacs, but it uses the `sendmail' command, which is
> symlinked to msmtp) and the only thing I miss from other mail clients
> is the ability to schedule a mail to be sent on a certain time. I do
> also use `message-mode' to compose my emails, and it has
> `gnus-delay-article' bound to C-c C-j, which at first sight seems to
> be exactly what I need.  The problem is, when calling it from
> =notmuch-message-mode= I get a `wrong-type-argument' from the first
> line of `gnus-agent-queue-setup',

I might be wrong, but I don't think the Gnus agent can be easily reused
from notmuch. We could perhaps save the message as a draft upon C-c C-j,
and then have a periodic timer that checks if any of the drafts has
expired (maybe using a special, additional tag for the search) and send
it?

Sounds doable, but maybe there's an easier way: what do more
knowleadgeble people think?

Cheers,
jao
-- 
Dealing with failure is easy: Work hard to improve. Success is also
easy to handle: You've solved the wrong problem. Work hard to improve.
  - Alan Perlis, Epigrams on Programming\r

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Scheduling mails
  2024-04-02  1:05   ` Jose A Ortega Ruiz
@ 2024-04-02  4:34     ` João Pedro
  2024-04-03 10:06       ` David Bremner
  0 siblings, 1 reply; 8+ messages in thread
From: João Pedro @ 2024-04-02  4:34 UTC (permalink / raw)
  To: Jose A Ortega Ruiz, notmuch

Em terça, 02/04/2024 às 02:05 (+01), Jose A Ortega Ruiz <jao@gnu.org> escreveu:

> I might be wrong, but I don't think the Gnus agent can be easily reused
> from notmuch. We could perhaps save the message as a draft upon C-c C-j,
> and then have a periodic timer that checks if any of the drafts has
> expired (maybe using a special, additional tag for the search) and send
> it?

I think we should be able to use a similar logic to
`gnus-delay-article': it adds an X-Gnus-Delayed header with a timestamp
and seems to, as you proposed, periodically check for scheduled
messages. The header holds a timestamp for the prompted scheduled time
(which can be given in as an absolute date, or relative to the current
time) and we could add to that a `scheduled' tag or something along
those lines.

-- 
João Pedro de A. Paula
IT bachelors at Universidade Federal do Rio Grande do Norte (UFRN)\r

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Scheduling mails
  2024-04-02  4:34     ` João Pedro
@ 2024-04-03 10:06       ` David Bremner
  2024-04-04  2:55         ` João Pedro
  0 siblings, 1 reply; 8+ messages in thread
From: David Bremner @ 2024-04-03 10:06 UTC (permalink / raw)
  To: João Pedro, Jose A Ortega Ruiz, notmuch

João Pedro <jpedrodeamorim@gmail.com> writes:

> Em terça, 02/04/2024 às 02:05 (+01), Jose A Ortega Ruiz <jao@gnu.org> escreveu:
>
>> I might be wrong, but I don't think the Gnus agent can be easily reused
>> from notmuch. We could perhaps save the message as a draft upon C-c C-j,
>> and then have a periodic timer that checks if any of the drafts has
>> expired (maybe using a special, additional tag for the search) and send
>> it?
>
> I think we should be able to use a similar logic to
> `gnus-delay-article': it adds an X-Gnus-Delayed header with a timestamp
> and seems to, as you proposed, periodically check for scheduled
> messages. The header holds a timestamp for the prompted scheduled time
> (which can be given in as an absolute date, or relative to the current
> time) and we could add to that a `scheduled' tag or something along
> those lines.

Message properties might work a bit better than tags, but your periodic
search would still have to search for all of the scheduled=time
properties (wildcard search is currently only supported using s-exp
queries).

I would suggest having a look at the existing draft handling, as what
you describe sounds like it is related.\r

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Scheduling mails
  2024-04-03 10:06       ` David Bremner
@ 2024-04-04  2:55         ` João Pedro
  2024-04-04 11:15           ` David Bremner
  0 siblings, 1 reply; 8+ messages in thread
From: João Pedro @ 2024-04-04  2:55 UTC (permalink / raw)
  To: David Bremner, Jose A Ortega Ruiz, notmuch

Em quarta, 03/04/2024 às 07:06 (-03), David Bremner <david@tethera.net> escreveu:

> Message properties might work a bit better than tags

Ah, indeed message properties seem to be more appropriate. Are they
persisted, or are they tied to an Emacs session? Because I would like to
have it so that even if I kill my Emacs session and open a new one, if
the message has the `:scheduled' property with the right time, it should
be sent. There's also concerns on what to do when Emacs was not up when
the scheduled time arrived. Do we 1. send it immediately? or 2. inform
the user it was not able to deliver the message? Going with the latter
has the caveat that it might be missed by the user, and one would be
clueless to the fact that their message hadn't arrived.

> but your periodic search would still have to search for all of the
> scheduled=time properties (wildcard search is currently only supported
> using s-exp queries).

How about having a list of '(MESSAGE-ID . SCHEDULED-TIME), with the
periodic check looking in that list for the scheduled times? It would
have the issue of it not being persistent between Emacs sessions, but I
think that could be addressed by letting the users know this lack of
persistance, and documenting how you could achieve it with
`savehist-additional-variables'.

> I would suggest having a look at the existing draft handling, as what
> you describe sounds like it is related.

Taking a quick glance at it, it seems like we could have a similar
thing, where scheduled messages are saved to the database somewhere,
with either the tag, as it is with drafts, or just the message property
as you suggested.

-- 
João Pedro de A. Paula
IT bachelors at Universidade Federal do Rio Grande do Norte (UFRN)\r

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Scheduling mails
@ 2024-04-04 11:10 Marc Fargas
  0 siblings, 0 replies; 8+ messages in thread
From: Marc Fargas @ 2024-04-04 11:10 UTC (permalink / raw)
  To: notmuch, jpedrodeamorim

[-- Attachment #1: Type: text/plain, Size: 1141 bytes --]

Hi there,

> I might be wrong, but I don't think the Gnus agent can be easily reused
> from notmuch. We could perhaps save the message as a draft upon C-c C-j,
> and then have a periodic timer that checks if any of the drafts has
> expired (maybe using a special, additional tag for the search) and send
> it?

I have solved this myself using `gnus-delay-article` as a basis.

I am attaching here my current code, it is my intention to (time
permitting) turn this over a patch for inclusion into notmuch itself.

What is preventing such patch right now:

- Writting tests,
- I'm trying to find something like `killing-new-buffers` builtin but
  will probably just include the code.

A side of the attached file you will need the `killing-new-buffers`
macro, pasted below.

(defmacro killing-new-buffers (&rest body)
  "Run BODY and kill any buffers that were not already open."
  (declare (debug t))
  (cl-with-gensyms (initial-buffers) 
    `(let ((,initial-buffers (buffer-list)))
       (unwind-protect
	   ,(macroexp-progn body)
	 (dolist (b (buffer-list)) (unless (memq b ,initial-buffers) (kill-buffer b)))))))

Hope this helps,

marc

[-- Attachment #2: notmuch-delay.el --]
[-- Type: application/emacs-lisp, Size: 6883 bytes --]

;;; notmuch-delay.el --- delay sending of mails in notmuch

;; Copyright (C) 2024 Marc Fargas <marc@marcfargas.com>

;; Author: Marc Fargas <marc@marcfargas.com>
;; Maintainer: Marc Fargas <marc@marcfargas.com>
;; Version: 20240311.0
;; URL: ???

;; This file is not part of GNU Emacs.

;;; Commentary:

;; Provide delayed sending of e-mails.

;; Heavily inspired by gnus-delay, mu4e-delay and mu4e-send-delay, but
;; made to work with notmuch. Thanks to Ben Maughen, Kai Großjohan and
;; Benjamin Andresen.

;;; Code:

(require 'notmuch)
(require 'notmuch-draft)
(autoload 'parse-time-string "parse-time" nil nil)

(defgroup notmuch-delay nil
  "Arrange for sending e-emails later."
  :group 'notmuch)

(defcustom notmuch-delay-tag "delayed"
  "Tag applied to delayed messages."
  :type 'string)

(defcustom notmuch-delay-header "X-Notmuch-Delayed"
  "Header name for storing info about delayed messages."
  :type 'string)

(defcustom notmuch-delay-default-delay "06:15"
  "Default length of delay."
  :type 'string)

(defcustom notmuch-delay-disabled-message-send-hooks '(org-mime-confirm-when-no-multipart)
  "`message-send-hook's that will be avoided during the delayed send.

But they would be run when postponing the message."
  :type 'list)

(defvar notmuch-delay-timer-scheduled nil
  "The current timer for the scheduled run.")

(defvar notmuch-delay-timer-idle nil
  "The current timer for the idle run.")


(defun notmuch-delay-parse-delay (delay)
  "Parse a delay string into a date. Possible values are:

* <digits><units> for <units> in minutes (`m'), hours (`h'), days (`d'),
  weeks (`w'), months (`M'), or years (`Y');

* YYYY-MM-DD for a specific date.  The time of day is given by the
  variable `gnus-delay-default-hour', minute and second are zero.

* hh:mm for a specific time.  Use 24h format.  If it is later than this
  time, then the deadline is tomorrow, else today."
  (let (num unit year month day hour minute deadline) ;; days
    (cond ((string-match
	    "\\([0-9][0-9][0-9]?[0-9]?\\)-\\([0-9]+\\)-\\([0-9]+\\)"
	    delay)
	   (setq year  (string-to-number (match-string 1 delay))
		 month (string-to-number (match-string 2 delay))
		 day   (string-to-number (match-string 3 delay)))
	   (setq deadline
		 (message-make-date
		  (encode-time 0 0      ; second and minute
			       gnus-delay-default-hour
			       day month year))))
	  ((string-match "\\([0-9]+\\):\\([0-9]+\\)" delay)
	   (setq hour   (string-to-number (match-string 1 delay))
		 minute (string-to-number (match-string 2 delay)))
	   ;; Use current time, except...
	   (setq deadline (decode-time nil nil t))
	   ;; ... for minute and hour.
	   (setq deadline (apply #'encode-time (car deadline) minute hour
				 (nthcdr 3 deadline)))
	   ;; If this time has passed already, add a day.
	   (when (time-less-p deadline nil)
	     (setq deadline (time-add 86400 deadline))) ; 86400 secs/day
	   ;; Convert seconds to date header.
	   (setq deadline (message-make-date deadline)))
	  ((string-match "\\([0-9]+\\)\\s-*\\([mhdwMY]\\)" delay)
	   (setq num (match-string 1 delay))
	   (setq unit (match-string 2 delay))
	   ;; Start from seconds, then multiply into needed units.
	   (setq num (string-to-number num))
	   (cond ((string= unit "Y")
		  (setq delay (* num 60 60 24 365)))
		 ((string= unit "M")
		  (setq delay (* num 60 60 24 30)))
		 ((string= unit "w")
		  (setq delay (* num 60 60 24 7)))
		 ((string= unit "d")
		  (setq delay (* num 60 60 24)))
		 ((string= unit "h")
		  (setq delay (* num 60 60)))
		 (t
		  (setq delay (* num 60))))
	   (setq deadline (message-make-date (time-add nil delay))))
	  (t (error "Malformed delay `%s'" delay)))))

;;;###autoload
(defun notmuch-delay-message (delay)
  "Delay this article by some time.
DELAY is a string, giving the length of the time. 
The value of `message-draft-headers' determines which headers are
generated when the article is delayed.  Remaining headers are
generated when the article is sent."
  (interactive (list (read-string
		      "Target date (YYYY-MM-DD), time (hh:mm), or length of delay (units in [mhdwMY]): "
		      notmuch-delay-default-delay))
	       message-mode)
  ;; Allow spell checking etc.
  (run-hooks 'message-send-hook)
  (let ((deadline (notmuch-delay-parse-delay delay)))
    (message (format "delay %s" deadline))
    (message-add-header (format "%s: %s" notmuch-delay-header deadline)))
  (set-buffer-modified-p t)
  (let ((notmuch-draft-tags (cons (format "+%s" notmuch-delay-tag) notmuch-draft-tags)))
    (notmuch-draft-save))
  (kill-buffer)
  (message-do-actions message-postpone-actions))

(defun notmuch-delay-get-delayed ()
  "Get the Message-IDs of currently delayed messages.

If DEADLINE is provided, return only those on which `notmuch-delay-header' is overdue."
  (let* ((query (format "is:%s" notmuch-delay-tag))
	 (delayed (notmuch-call-notmuch-sexp
		   "search" "--format=sexp" "--format-version=5" "--output=messages"
		   query)))
    (cl-mapcar (lambda (el) (format "id:%s" el)) delayed)))

;;;###autoload
(defun notmuch-delay-send-queue ()
  "Send all the delayed messages that are due now."
  (interactive)
  (let* ((delayed (notmuch-delay-get-delayed))
	 (message-send-hook (seq-difference message-send-hook
					    notmuch-delay-disabled-message-send-hooks))
	 (message-confirm-send nil))
    (add-hook 'message-send-hook
	      (lambda () (message-remove-header notmuch-delay-header)) t)
    (save-window-excursion
      (killing-new-buffers
	  (while-let ((message (pop delayed)))
	    (notmuch-draft-resume message)
	    (goto-char (point-min))
	    (if (re-search-forward
		 (concat "^" (regexp-quote notmuch-delay-header) ":\\s-+")
		 nil t)
		(progn
		  (setq deadline-str (buffer-substring (point) (line-end-position)))
		  (setq deadline (encode-time (parse-time-string deadline-str)))
		  (if (time-less-p deadline nil)
		      (progn
			(message "Sending delayed message %s" message)
			(notmuch-mua-send-and-exit)
			(message "Sending delayed message %s...done" message))
		    (message "Not yet ready to send %s until %s" message deadline-str)
		    (kill-buffer)))
	      (message "Delay header missing for message %s" message)))))
    ))

(defun notmuch-delay-scheduled-runner ()
  (unless (memq notmuch-delay-timer-idle timer-idle-list)
    (setq notmuch-delay-timer-idle 
	  (run-with-idle-timer 30 nil #'notmuch-delay-send-queue))))

;;;###autoload
(defun notmuch-delay-initialize ()
  (unless (memq notmuch-delay-timer-scheduled timer-list)
    (setq notmuch-delay-timer-scheduled
	  (run-at-time t 300 #'notmuch-delay-scheduled-runner))))

(provide 'notmuch-delay)

;; Local Variables:
;; coding: utf-8
;; End:

;;; notmuch-delay.el ends here



[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Scheduling mails
  2024-04-04  2:55         ` João Pedro
@ 2024-04-04 11:15           ` David Bremner
  0 siblings, 0 replies; 8+ messages in thread
From: David Bremner @ 2024-04-04 11:15 UTC (permalink / raw)
  To: João Pedro, Jose A Ortega Ruiz, notmuch

João Pedro <jpedrodeamorim@gmail.com> writes:

> Ah, indeed message properties seem to be more appropriate. Are they
> persisted, or are they tied to an Emacs session?

They are persisted in the database, and backed up with notmuch dump.
>
>> but your periodic search would still have to search for all of the
>> scheduled=time properties (wildcard search is currently only supported
>> using s-exp queries).
>
> How about having a list of '(MESSAGE-ID . SCHEDULED-TIME), with the
> periodic check looking in that list for the scheduled times? It would
> have the issue of it not being persistent between Emacs sessions, but I
> think that could be addressed by letting the users know this lack of
> persistance, and documenting how you could achieve it with
> `savehist-additional-variables'.

One advantage of doing it outside emacs (in the notmuch database) is
that you could have the delivery also managed outside emacs. So cron /
systemd-timer / whatever.  I guess the downside is it might be more work
to prototype.

\r

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Scheduling mails
  2024-04-02  0:18 ` Scheduling mails João Pedro
  2024-04-02  1:05   ` Jose A Ortega Ruiz
@ 2024-04-04 16:38   ` Marc Fargas
  1 sibling, 0 replies; 8+ messages in thread
From: Marc Fargas @ 2024-04-04 16:38 UTC (permalink / raw)
  To: jpedrodeamorim; +Cc: notmuch

[-- Attachment #1: Type: text/plain, Size: 1141 bytes --]

Hi there,

> I might be wrong, but I don't think the Gnus agent can be easily reused
> from notmuch. We could perhaps save the message as a draft upon C-c C-j,
> and then have a periodic timer that checks if any of the drafts has
> expired (maybe using a special, additional tag for the search) and send
> it?

I have solved this myself using `gnus-delay-article` as a basis.

I am attaching here my current code, it is my intention to (time
permitting) turn this over a patch for inclusion into notmuch itself.

What is preventing such patch right now:

- Writting tests,
- I'm trying to find something like `killing-new-buffers` builtin but
  will probably just include the code.

A side of the attached file you will need the `killing-new-buffers`
macro, pasted below.

(defmacro killing-new-buffers (&rest body)
  "Run BODY and kill any buffers that were not already open."
  (declare (debug t))
  (cl-with-gensyms (initial-buffers) 
    `(let ((,initial-buffers (buffer-list)))
       (unwind-protect
	   ,(macroexp-progn body)
	 (dolist (b (buffer-list)) (unless (memq b ,initial-buffers) (kill-buffer b)))))))

Hope this helps,

marc

[-- Attachment #2: notmuch-delay.el --]
[-- Type: application/emacs-lisp, Size: 6883 bytes --]

;;; notmuch-delay.el --- delay sending of mails in notmuch

;; Copyright (C) 2024 Marc Fargas <marc@marcfargas.com>

;; Author: Marc Fargas <marc@marcfargas.com>
;; Maintainer: Marc Fargas <marc@marcfargas.com>
;; Version: 20240311.0
;; URL: ???

;; This file is not part of GNU Emacs.

;;; Commentary:

;; Provide delayed sending of e-mails.

;; Heavily inspired by gnus-delay, mu4e-delay and mu4e-send-delay, but
;; made to work with notmuch. Thanks to Ben Maughen, Kai Großjohan and
;; Benjamin Andresen.

;;; Code:

(require 'notmuch)
(require 'notmuch-draft)
(autoload 'parse-time-string "parse-time" nil nil)

(defgroup notmuch-delay nil
  "Arrange for sending e-emails later."
  :group 'notmuch)

(defcustom notmuch-delay-tag "delayed"
  "Tag applied to delayed messages."
  :type 'string)

(defcustom notmuch-delay-header "X-Notmuch-Delayed"
  "Header name for storing info about delayed messages."
  :type 'string)

(defcustom notmuch-delay-default-delay "06:15"
  "Default length of delay."
  :type 'string)

(defcustom notmuch-delay-disabled-message-send-hooks '(org-mime-confirm-when-no-multipart)
  "`message-send-hook's that will be avoided during the delayed send.

But they would be run when postponing the message."
  :type 'list)

(defvar notmuch-delay-timer-scheduled nil
  "The current timer for the scheduled run.")

(defvar notmuch-delay-timer-idle nil
  "The current timer for the idle run.")


(defun notmuch-delay-parse-delay (delay)
  "Parse a delay string into a date. Possible values are:

* <digits><units> for <units> in minutes (`m'), hours (`h'), days (`d'),
  weeks (`w'), months (`M'), or years (`Y');

* YYYY-MM-DD for a specific date.  The time of day is given by the
  variable `gnus-delay-default-hour', minute and second are zero.

* hh:mm for a specific time.  Use 24h format.  If it is later than this
  time, then the deadline is tomorrow, else today."
  (let (num unit year month day hour minute deadline) ;; days
    (cond ((string-match
	    "\\([0-9][0-9][0-9]?[0-9]?\\)-\\([0-9]+\\)-\\([0-9]+\\)"
	    delay)
	   (setq year  (string-to-number (match-string 1 delay))
		 month (string-to-number (match-string 2 delay))
		 day   (string-to-number (match-string 3 delay)))
	   (setq deadline
		 (message-make-date
		  (encode-time 0 0      ; second and minute
			       gnus-delay-default-hour
			       day month year))))
	  ((string-match "\\([0-9]+\\):\\([0-9]+\\)" delay)
	   (setq hour   (string-to-number (match-string 1 delay))
		 minute (string-to-number (match-string 2 delay)))
	   ;; Use current time, except...
	   (setq deadline (decode-time nil nil t))
	   ;; ... for minute and hour.
	   (setq deadline (apply #'encode-time (car deadline) minute hour
				 (nthcdr 3 deadline)))
	   ;; If this time has passed already, add a day.
	   (when (time-less-p deadline nil)
	     (setq deadline (time-add 86400 deadline))) ; 86400 secs/day
	   ;; Convert seconds to date header.
	   (setq deadline (message-make-date deadline)))
	  ((string-match "\\([0-9]+\\)\\s-*\\([mhdwMY]\\)" delay)
	   (setq num (match-string 1 delay))
	   (setq unit (match-string 2 delay))
	   ;; Start from seconds, then multiply into needed units.
	   (setq num (string-to-number num))
	   (cond ((string= unit "Y")
		  (setq delay (* num 60 60 24 365)))
		 ((string= unit "M")
		  (setq delay (* num 60 60 24 30)))
		 ((string= unit "w")
		  (setq delay (* num 60 60 24 7)))
		 ((string= unit "d")
		  (setq delay (* num 60 60 24)))
		 ((string= unit "h")
		  (setq delay (* num 60 60)))
		 (t
		  (setq delay (* num 60))))
	   (setq deadline (message-make-date (time-add nil delay))))
	  (t (error "Malformed delay `%s'" delay)))))

;;;###autoload
(defun notmuch-delay-message (delay)
  "Delay this article by some time.
DELAY is a string, giving the length of the time. 
The value of `message-draft-headers' determines which headers are
generated when the article is delayed.  Remaining headers are
generated when the article is sent."
  (interactive (list (read-string
		      "Target date (YYYY-MM-DD), time (hh:mm), or length of delay (units in [mhdwMY]): "
		      notmuch-delay-default-delay))
	       message-mode)
  ;; Allow spell checking etc.
  (run-hooks 'message-send-hook)
  (let ((deadline (notmuch-delay-parse-delay delay)))
    (message (format "delay %s" deadline))
    (message-add-header (format "%s: %s" notmuch-delay-header deadline)))
  (set-buffer-modified-p t)
  (let ((notmuch-draft-tags (cons (format "+%s" notmuch-delay-tag) notmuch-draft-tags)))
    (notmuch-draft-save))
  (kill-buffer)
  (message-do-actions message-postpone-actions))

(defun notmuch-delay-get-delayed ()
  "Get the Message-IDs of currently delayed messages.

If DEADLINE is provided, return only those on which `notmuch-delay-header' is overdue."
  (let* ((query (format "is:%s" notmuch-delay-tag))
	 (delayed (notmuch-call-notmuch-sexp
		   "search" "--format=sexp" "--format-version=5" "--output=messages"
		   query)))
    (cl-mapcar (lambda (el) (format "id:%s" el)) delayed)))

;;;###autoload
(defun notmuch-delay-send-queue ()
  "Send all the delayed messages that are due now."
  (interactive)
  (let* ((delayed (notmuch-delay-get-delayed))
	 (message-send-hook (seq-difference message-send-hook
					    notmuch-delay-disabled-message-send-hooks))
	 (message-confirm-send nil))
    (add-hook 'message-send-hook
	      (lambda () (message-remove-header notmuch-delay-header)) t)
    (save-window-excursion
      (killing-new-buffers
	  (while-let ((message (pop delayed)))
	    (notmuch-draft-resume message)
	    (goto-char (point-min))
	    (if (re-search-forward
		 (concat "^" (regexp-quote notmuch-delay-header) ":\\s-+")
		 nil t)
		(progn
		  (setq deadline-str (buffer-substring (point) (line-end-position)))
		  (setq deadline (encode-time (parse-time-string deadline-str)))
		  (if (time-less-p deadline nil)
		      (progn
			(message "Sending delayed message %s" message)
			(notmuch-mua-send-and-exit)
			(message "Sending delayed message %s...done" message))
		    (message "Not yet ready to send %s until %s" message deadline-str)
		    (kill-buffer)))
	      (message "Delay header missing for message %s" message)))))
    ))

(defun notmuch-delay-scheduled-runner ()
  (unless (memq notmuch-delay-timer-idle timer-idle-list)
    (setq notmuch-delay-timer-idle 
	  (run-with-idle-timer 30 nil #'notmuch-delay-send-queue))))

;;;###autoload
(defun notmuch-delay-initialize ()
  (unless (memq notmuch-delay-timer-scheduled timer-list)
    (setq notmuch-delay-timer-scheduled
	  (run-at-time t 300 #'notmuch-delay-scheduled-runner))))

(provide 'notmuch-delay)

;; Local Variables:
;; coding: utf-8
;; End:

;;; notmuch-delay.el ends here



[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2024-04-04 16:39 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <QgHQAeKcfzu4V_C0tkbpYXnzqYi9qt3ZvrB6ld7HkVYnD1tPjYaOTvwZZ9OEQVC8un-tb6uJflqJojN6lXLNJw==@protonmail.internalid>
2024-04-02  0:18 ` Scheduling mails João Pedro
2024-04-02  1:05   ` Jose A Ortega Ruiz
2024-04-02  4:34     ` João Pedro
2024-04-03 10:06       ` David Bremner
2024-04-04  2:55         ` João Pedro
2024-04-04 11:15           ` David Bremner
2024-04-04 16:38   ` Marc Fargas
2024-04-04 11:10 Marc Fargas

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).