I use mu4e for email with multiple accounts. I have multiple accounts in mu4e and followed the manual with a function my-mu4e-set-account and a hook to it:

(add-hook 'mu4e-compose-pre-hook 'my-mu4e-set-account)

I can start a message with

(compose-mail "To <to@to>" "subject" '(("From" . "me <me@me>")))

I get queried twice about which account to use:

[mu4e] Select context: [A]ccount1 [S]omeOther
Compose with account: (Account1/SomeOther)

and then the "From" of my choice is overwritten by the selection from mu4e.

How can I set the "From" header programmatically without mu4e asking for the context twice? And how can I pre-fill the body of the email, since compose-mail only takes headers in the other-headers argument and the body is not a header?

Update: Following the comments, here is a scenario. I have a list of 20 email addresses to which I want to send a similar email with a custom To: and the name in the body. I would like to send those twenty emails programmatically from mu4e:

  • initiate the email
  • set the right context with the From: address
  • set the subject
  • set the custom recipient
  • set the body, customized to the recipient
  • send the email

At the moment, the interactive prompts interrupt me at step 2 of setting the From: address. The second prompt was my blunder from adding a function from the manual:

(defun my-mu4e-set-account ()
  "Set the account for composing a message.
   This function is taken from: 
  (let* ((account
    (if mu4e-compose-parent-message
        (let ((maildir (mu4e-message-field mu4e-compose-parent-message :maildir)))
          (string-match "/\\(.*?\\)/" maildir)
          (match-string 1 maildir))
      (completing-read (format "Compose with account: (%s) "
                               (mapconcat #'(lambda (var) (car var))
                                          my-mu4e-account-alist "/"))
                       (mapcar #'(lambda (var) (car var)) my-mu4e-account-alist)
                       nil t nil nil (caar my-mu4e-account-alist))))
         (account-vars (cdr (assoc account my-mu4e-account-alist))))
    (if account-vars
        (mapc #'(lambda (var)
                  (set (car var) (cadr var)))
      (error "No email account found"))))
(add-hook 'mu4e-compose-pre-hook 'my-mu4e-set-account)

From the comments, I can set the body with message-goto-body and insert. So the only other question is how to set the right context programmatically. I disabled the context switch hooks and tried this code:

(compose-mail "" "" '(("From" . "me <me@domain.com>")))

and I get a draft from the last used context email address and a body with a mysterious 158 in it.

Second update: I read the Contexts part of the manual and am still unclear on how to set the right context. The variables mu4e-contexts and my-mu4e-account-alist are set to my list of accounts, e.g. Account1 and SomeOther, and I don't know how to tell mu4e to pick the one that matches a string such as SomeOther. I tried

(setq mu4e-compose-pre-hook nil)
(mu4e-context-switch nil "SomeOther")

I compose a new message with C-x m and still get a draft from Account1.

Although the suggestion of auto-answer in a comment could work, I believe a more straightforward solution exists.

  • please describe the scenario a little more to clarify what you're after. For what it seems, using compose-mail, I'd say that your troubles will be solved with any of the skeletons to fill the body creating the draft from mu4e. I mean, how you get the To: field? you want to draft only new mail? replies to messages in the inbox?. Also it seems like you don't have a default context, and why mu4e is asking. Once you have the buffer up, message-goto-body and insert will be enough. – Muihlinn Jul 13 at 11:40
  • Maybe (let ((mu4e-compose-context-policy 'pick-first)) ...) or set it to nil. I think there's a bug in the version I'm using so this is only a guess. – jagrg Jul 15 at 0:02
  • @Muihlinn You're right, I could use message-goto-body to set the body of the email. That leaves selecting the context programmatically and I added a clarification. – miguelmorin Aug 3 at 16:11
  • @jagrg Using (setq mu4e-compose-context-policy nil) skipped the prompt, which shows it's the right approach. How can I set the context programmatically? – miguelmorin Aug 3 at 16:25
  • This is not a solution, but a workaround: you can consider using auto-answer to deal with the prompt. This answer shows how to do this. In short, bind the variable auto-answer to an alist of the form (prompt . answer) and the interactive prompt matching the ones in the alist will be answered with the associated to the matched prompt. – Firmin Martin Aug 3 at 20:31

Something as simple as this snippet will work, the only pitfall is that mu4e should be loaded before trying to select a context or it'll fail. From here you can "complicate" the thing as much as you need.

The string used to call the context is the :name you used to define it. Also you could get it from mu4e-contexts variable. All the rest is pretty straightforward.

(defun compose-message (context to subject body)
  (let ((mu4e-compose-context-policy nil))
    (mu4e-context-switch t context)
    (compose-mail to subject)
    (insert body)))

Stopped there because in fact you don't want to "draft" in the sense of storing it into draft mail, just to precompose an email automatically, probably check it out, then send.

| improve this answer | |
  • The first line seems to run successfully: (mu4e-context-switch t "SomeOther") in the scratch buffer shows #s(mu4e-context "SomeOther" nil nil (lambda (msg) (if msg (progn ...))) ((mu4e-trash-folder . "/SomeOther/Deleted Items") (mu4e-refile-folder . "/SomeOther/Deleted Items"))). Yet, the second line drafts the email from the default account, Account1. – miguelmorin 2 days ago
  • That is what the function should return in the scratch buffer. What I don't know is why it didn't changed the context, worked as-is with all my 6 accounts. Also, if the context doesn't exists it signals the error, so I'm puzzled. If you switch the context from mu4e ; and compose does it change and drafts using it?. BTW I tested it with 1.4.10. – Muihlinn 2 days ago
  • If I switch the context within mu4e with ;, it shows the new context, yet the new message has a from: of the default context. I have set (setq mu4e-compose-context-policy nil). Is it only a matter of changing the From: address? – miguelmorin 2 days ago
  • 1
    no, AFAIK it takes all the references from the context, it'll go through, and will be filed, under different mailboxes and sent from the selected context account. As q&d test, save it as draft and look at what draft mailbox is filed. I'd say that there is something wrong somewhere in your config, perhaps the from is the same in both configs and it's fooling you. Context part in the modeline should tell you. – Muihlinn 2 days ago
  • Thanks for the debugging help. You're right, this is a problem beyond your code snippet. I asked about it at emacs.stackexchange.com/questions/59986/… and will test your solution once I fix that. – miguelmorin yesterday

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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