1

I have a custom citation in mu4e to include the date and time:

;; Cite messages with date and time of original
(setq message-citation-line-function 'message-insert-formatted-citation-line)
(setq message-citation-line-format "\n\nOn %a, %d %b %Y at %R %Z, %N wrote:\n")

When I forward an email, I would like to include the recipient of the original message I am forwarding. (When I reply, it doesn't need to include it, but no harm in doing so.) I tried all lower-case and upper-case letters and none includes the original recipient. The docs and Add date to mu4e email when forwarding or replying inspired this customization but do not mention the original recipient.

Is it possible to include the original recipient of a message in the citation?

update

With the above code, when I forward an email that I wrote, I see:

On Sat, 18 Dec 2021 at 18:41 WET, Miguel M... <...@...> wrote:

I would code so it shows:

On Sat, 18 Dec 2021 at 18:41 WET, Miguel M... <...@...> wrote to Recipient 1 <...@....>:
6
  • 1
    When I reply or forward an email my citation always has the name of the recipient, denoted by the %R in your example. Perhaps you can add an example of what you want/expect to achieve?
    – Ajned
    Dec 19, 2021 at 14:36
  • The %R in my case gives the timestamp. I updated the question with an example of what I see and what I would like.
    – emonigma
    Dec 20, 2021 at 14:29
  • Can you try wrote to %f instead?
    – Ajned
    Dec 20, 2021 at 15:45
  • 1
    nevermind the %R (that was incorrect) but did %f do the trick? It should return the name of the recipient
    – Ajned
    Dec 21, 2021 at 12:28
  • 1
    I think I finally understood the problem. You want to forward a message and see whoever is in the To: field displayed in the citation line. Am I correct? A sub problem is that you can have multiple recipients (instead of just one)? What to do in that case?
    – Ajned
    Dec 25, 2021 at 6:53

2 Answers 2

2

This solution does works for me although it might have other undesirable side-effects; we are redefining a compiled emacs function message-insert-formatted-citation-line. It assumes that you are using gnus and therefore the name of the mu4e-view buffer where messages are read should be *Article*.

(defun extrecip ()
  "This function extracts the name of the message 1st recipient's"
  (nth 1 (nth 0 (mu4e-message-field (mu4e-message-at-point) :to))))
(defun mclfwor (orecipient)
  "This function outputs message-citation-line-format with original recipient"
  (concat "\n\nOn \%a, \%d \%b \%Y at \%R \%Z, \%N wrote to " orecipient ":\n"))

(defun message-insert-formatted-citation-line (&optional from date tz)
  "Function that inserts a formatted citation line.
The optional FROM, and DATE are strings containing the contents of
the From header and the Date header respectively.
The optional TZ is omitted or nil for Emacs local time, t for
Universal Time, `wall' for system wall clock time, or a string as
in the TZ environment variable.  It can also be a list (as from
`current-time-zone') or an integer (as from `decode-time')
applied without consideration for daylight saving time.
See `message-citation-line-format'."
  ;; The optional args are for testing/debugging.  They will disappear later.
  ;; Example:
  ;; (with-temp-buffer
  ;;   (message-insert-formatted-citation-line
  ;;    "John Doe <[email protected]>"
  ;;    (message-make-date))
  ;;   (buffer-string))
  (when (or message-reply-headers (and from date))
    (unless from
      (setq from (mail-header-from message-reply-headers)))
    (let* ((data (ignore-errors
                   (funcall (or (bound-and-true-p
                                 gnus-extract-address-components)
                                #'mail-extract-address-components)
                            from)))
       (name (car data))
       (fname name)
       (lname name)
           (net (cadr data))
           (name-or-net (or name net from))
       (orecipient (with-current-buffer "*Article*" (extrecip)))
       (time
            (when (string-match-p "%[^FLNfn]" (funcall 'mclfwor orecipient))
          (cond ((numberp (car-safe date)) date) ;; backward compatibility
            (date (gnus-date-get-time date))
            (t
             (gnus-date-get-time
              (setq date (mail-header-date message-reply-headers)))))))
       (tz (or tz
           (when (stringp date)
             (nth 8 (parse-time-string date)))))
           spec)
      (when (stringp name)
        ;; Guess first name and last name:
        (let* ((names (seq-filter
                       (lambda (s)
                         (string-match-p (rx bos (+ (in word ?. ?-)) eos) s))
                       (split-string name "[ \t]+")))
               (count (length names)))
          (cond ((= count 1)
                 (setq fname (car names)
                       lname ""))
                ((or (= count 2) (= count 3))
                 (setq fname (car names)
                       lname (string-join (cdr names) " ")))
                ((> count 3)
                 (setq fname (string-join (butlast names (- count 2))
                                          " ")
                       lname (string-join (nthcdr 2 names) " "))))
          (when (string-match "\\(.*\\),\\'" fname)
            (let ((newlname (match-string 1 fname)))
              (setq fname lname lname newlname)))))
      ;; The following letters are not used in `format-time-string':
      (push (cons ?E "<E>") spec)
      (push (cons ?F (or fname name-or-net)) spec)
      ;; We might want to use "" instead of "<X>" later.
      (push (cons ?J "<J>") spec)
      (push (cons ?K "<K>") spec)
      (push (cons ?L lname) spec)
      (push (cons ?N name-or-net) spec)
      (push (cons ?O "<O>") spec)
      (push (cons ?P "<P>") spec)
      (push (cons ?Q "<Q>") spec)
      (push (cons ?f from) spec)
      (push (cons ?i "<i>") spec)
      (push (cons ?n net) spec)
      (push (cons ?o "<o>") spec)
      (push (cons ?q "<q>") spec)
      (push (cons ?t "<t>") spec)
      (push (cons ?v "<v>") spec)
      ;; Delegate the rest to `format-time-string':
      (dolist (c (nconc (number-sequence ?A ?Z)
                        (number-sequence ?a ?z)))
        (unless (assq c spec)
          (push (cons c (condition-case nil
                            (format-time-string (format "%%%c" c) time tz)
                          (error (format ">%c<" c))))
                spec)))
      (insert (format-spec (funcall 'mclfwor orecipient) spec)))
    (newline)))

Previous answer

Probably there is a more elegant way to solve this problem, but I have developed a function which extracts the name of the first recipient of a message

(defun extrecip ()
  "This function extracts the name of the message 1st recipient's"
  (interactive)
  (message (car (car (mu4e-message-field (mu4e-message-at-point) :to)))))

If you call it within a message it should output the name of the recipient. The only part left would be to connect it somehow to message-citation-line-format.

4
  • The %f is the same as %N but it includes the email address. When I use %f, I see On Sat, 18 Dec 2021 at 18:41 WET, Miguel M... wrote to Miguel M... <...@...> and I would like instead ... wrote to Ajned ... <...@...>.
    – emonigma
    Dec 26, 2021 at 10:00
  • Yes, this function extracts the 1st recipient's name, and a small adjustment would extract the email ((cdr (car (mu4e-message-field (mu4e-message-at-point) :to)))). Do you have an idea how to connect it to message-citation-line-format?
    – emonigma
    Jan 12, 2022 at 15:33
  • @miguelmorin I have added a complete solution now
    – Ajned
    Aug 1, 2022 at 8:59
  • I tested this solution and it seems to work, but I must say that I've now paused using mu4e.
    – emonigma
    Aug 1, 2022 at 10:40
1

Since you are using mu4e, you could also do the following:

  • Add a citation line with message-citation-line-format which acts as a place keeper. In my example, this is "MU4E: REPLACE THIS CITATION LINE!".
  • Replace this place keeper in mu4e-compose-mode-hook with something according to your needs. The advantage is that you can access fields of the parent email with mu4e-compose-parent-message and mu4e-message-field.

Here is a minimal example how it works:

(setq message-citation-line-format "MU4E: REPLACE THIS CITATION LINE!")
(defun tl/mail-address-plist-to-string (address)
  "Convert the plist ADDRESS with keys :name and :email into a string."
  (let ((email (plist-get address ':email))
            (name  (plist-get address ':name)))
    (concat
     (or name email)
     " "
     "<" email ">")))

(defun tl/message-citation-line-string ()
  "Generate citation text for current message with mu4e functions."
  (let* ((msg mu4e-compose-parent-message)
             (from (mu4e-message-field msg :from))
             (to (mu4e-message-field msg :to))
             (cc (mu4e-message-field msg :cc))
             (subject (mu4e-message-field msg :subject))
             (date (mu4e-message-field msg :date)))
    (concat "From: " (mapconcat 'tl/mail-address-plist-to-string from ", ") "\n"
                    "Date: " (format-time-string "%A, %Y-%m-%d, at %H:%M" date) "\n"
                    "To: " (mapconcat 'tl/mail-address-plist-to-string to ", ") "\n"
                    (when cc (concat "Cc: " (mapconcat 'tl/mail-address-plist-to-string cc ", ") "\n"))
                    "Subject: " subject
                    )))

(add-hook 'mu4e-compose-mode-hook
                (defun my-mu4e-compose-settings ()
                    "My settings for message composition."
                        
                    ;; Replace citation line place keeper.
                        (save-excursion
                            (beginning-of-buffer)
                            (when (re-search-forward message-citation-line-format nil t)
                                (replace-match (tl/message-citation-line-string) t)))
                    ))
1
  • Thank you Tim. Unfortunately, I no longer use mu4e and so cannot test your answer.
    – emonigma
    May 13, 2023 at 10:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.