Emacs收发邮件完全操作指南: Send-Mail, Rmail and Gnus

本文为项目"步步为营, 零秒精通Emacs"

Appendix D: Email Management

  • Sending Mails from QQ.com
  • Rmail
  • Gnus: 1.Fundermental Configuration
  • Gnus: 2.Concepts
  • Gnus: 3.Group Buffer
  • Gnus: 4.Summary Buffer
  • Gnus: 5.Article Buffer
  • Gnus: 6.Completed Configuration in Progress

1.Sending Mails From @qq.com

从qq邮箱的设置中开启SMTP等服务

Config SMTP

从.emacs中添加配置:

(setq message-send-mail-function 'smtpmail-send-it)
(setq user-mail-address "abst.proc.do@qq.com")
(setq user-full-name "abst.proc.do")

(setq smtpmail-smtp-user "abst.proc.do@qq.com"
      smtpmail-smtp-server "smtp.qq.com"
      smtpmail-smtp-service 465
      smtpmail-stream-type 'ssl)

;;Debug
(setq smtpmail-debug-info t)
(setq smtpmail-debug-verb t)

Authorization

短信获取qq邮箱的授权码后, 新建文件 .authinfo

machine smtp.qq.com  login abst.proc.do@qq.com password 授权码(比如abcd等)

配置文件中添加

(require 'auth-source);; probably not necessary
(setq auth-sources '("~/.authinfo" "~/.authinfo.gpg"))
;;(customize-variable 'auth-sources) ;; optional, do it once

Testing

此时完整的配置文件为:

;;Sending Email

(require 'auth-source);; probably not necessary
(setq auth-sources '("~/.authinfo" "~/.authinfo.gpg"))
;;(customize-variable 'auth-sources) ;; optional, do it once

(setq message-send-mail-function 'smtpmail-send-it)
(setq user-mail-address "abst.proc.do@qq.com")
(setq user-full-name "abst.proc.do")

(setq smtpmail-smtp-user "abst.proc.do@qq.com"
      smtpmail-smtp-server "smtp.qq.com"
      smtpmail-smtp-service 465
      smtpmail-stream-type 'ssl)

;;Debug
(setq smtpmail-debug-info t)
(setq smtpmail-debug-verb t)

C-x m (Compose-mail)

C-c C-c (message-send-and-exit) 提示发送成功后, 检查qq邮箱.

上述冗余配置能保证邮件发送百分百成功.

Mail Commands

  • C-c C-c Send the message, and bury the mail buffer (message-send-and-exit).

  • C-c C-s Send the message, and leave the mail buffer selected (message-send).

  • mail-from-style p- parens Use both address and full name, as in: ‘king@grassland.com (Elvis Parsley)’.

小结

先发出去邮件, 后面的操作便能水到渠成 Emacs Manual的32.Sending-Mail有六个小节.

2.Rmail

Experience movemail

Rmail的后端调用movemail, 因此先手工体验一般movemail

sudo apt-get install mailutils

命令行中运行

movemail -v 'imaps://abst.proc.do:授权码@imap.qq.com:993' ~/RMAIL

或者在 .bashrc 中alias

alias rmail="movemail -v 'imaps://abst.proc.do:授权码@imap.qq.com:993' ~/RMAIL"

运行后

$ rmail
movemail: number of messages in source mailbox: 1
movemail: number of processed messages: 1
movemail: number of errors: 0 / 0

View RMAIL File

此时查看Rmail文件

cat ~/RMAIL | head -20

读到的是普通的文本, 而且decode也有问题, 重置 rmail-file-coding-system

(setq rmail-file-coding-system t)

然后在 ~/RMAIL 中启动 rmail-mode,

邮件的基本操作:

  • Moving Among Mails

    • n 查看下一封邮件

    Move to the next nondeleted message, skipping any intervening deleted messages (rmail-next-undeleted-message).

    • p 查看上一封有家

    Move to the previous nondeleted message (rmail-previous-undeleted-message).

  • Reply Mails

    • m

    Send a message (rmail-mail).

    • c

    Continue editing the already started outgoing message (rmail-continue).

    • r

    Send a reply to the current Rmail message (rmail-reply).

    • f

    Forward the current message to other users (rmail-forward).

    • C-u f

    Resend the current message to other users (rmail-resend).

    • M-m

    Try sending a bounced message a second time (rmail-retry-failure).

Manually Rmail workflow

总结以上手工Rmail的工作流: 由于配置 .bashrc

alias rmail="movemail -v 'imaps://abst.proc.do:授权码@imap.qq.com:993' ~/RMAIL"

因此可以

  1. 从shell中运行rmail, 更新收件箱 ~/RMAIL,
  2. rmail-mode模式下查看 ~/RMAIL

Automatic Rmail

将Rmail workflow的第一步合并进第二步中, 从配置中添加:

;; Reading Mail
(setq rmail-primary-inbox-list
      '("imaps://abst.proc.do:授权码@imap.qq.com:993")
      )
(setq rmail-movemail-program "/usr/bin/movemail")

load-file后, M-x rmail 运行的message

new messages...done (1)
Saving file /home/gaowei/RMAIL...
Wrote /home/gaowei/RMAIL [2 times]
1 new message read
Quit

小结

Sending Email与Rmail, 可以应用Emacs自如的收发邮件, 但并不能有效的管理邮件.

对邮件进行管理需要引入更加便利的自动化工具gnus

7赞

Gnus: 1.Fundermental Configuration

Gnus的基本配置很简单, 只需要几行代码 新建文件 ~/.gnus 然后填入以下代码

(setq user-mail-address "abst.proc.do@qq.com"
      user-full-name "abst.proc.do")

(setq gnus-select-method
      '(nnimap "qq.com"
               (nnimap-address "imap.qq.com")
               (nnimap-inbox "INBOX")
               (nnimap-expunge t)
               (nnimap-server-port 993)
               (nnimap-stream ssl)))

(setq send-mail-function 'smtpmail-send-it
      smtpmail-smtp-server "smtp.qq.com"
      smtpmail-smtp-service 465
      smtpmail-stream-type 'ssl
      gnus-ignored-newsgroups "^to\\.\\|^[0-9. ]+\\( \\|$\\)\\|^[\"]\"[#'()]")

并在 .authinfo 中加入一行imap的配置,

machine imap.qq.com  login abst.proc.do@qq.com password 授权码
machine smtp.qq.com  login abst.proc.do@qq.com password 授权码

仅此而已, 便能开工 M-x gnus 调出调出邮件文件夹界面

提示收到inbox收到2封邮件 也可以 L (gnus-group-list-all-group)查看所有的邮箱文件夹 gnus-group1

INBOX 上按键 Space 查看收件箱

退出Gnus

在Group buffer中按键q退出gnus.

Gnus: 2.Concepts

Gnus有三个基础概念,
Group Buffer, Summary Buffer and Article Buffer
分别对应邮件的 1)分类文件夹(收件箱) 2) 邮件列表 3) 邮件正文

20200213-173230

Group Buffers

Summary Buffer

Article Buffer

Gnus: 3.Group Buffer

Window Layout

M-x gnus默认"use-full-window", 会删掉当前的其他窗口而占据整个屏幕, 因为 gnus-use-full-window 默认设置为 t, 这会带来诸多不便. 关闭"use-full-window"的模式

(setq gnus-use-full-window nil)

Visualize Groups

Group Buffer默认只显示有未读邮件的Group,

查看其他邮件组, 有四种方法

  1. 在Group buffer中按键 “j”:(gnus-group-jump-to-group), 选择Group后, 并能从Group Buffer中可见

2.Group Buffer中按键 “L”:(gnus-group-list-all-groups &optional ARG)

3.指定可见的邮件组, 从步骤二中的选择需要的Group, 按键G p, 弹出的对话框中加入一行 (visible . t)

此时重新连接gnus, Group Buffer中按键 Z R, gnus-summary-reselect-current-group 重新定义的Group便能显示在初始界面.

4.从Server中查看 Group Buffer中按键 ^ (gnus-group-enter-server-mode), 在 “{nnimap:qq.com} (opened)” 上按键Space

也可以一步进入Server Group Buffer: A A (gnus-group-list-active)

Archive Messages

Gnus的默认配置, 生成 "sent.%Y-%m" 格式的 Send-Mail存档, 这与imap的Send-Messages重复, 因此关闭改功能

(setq gnus-message-archive-group nil)

New Group

创建新的邮件组

G m : Make a new group (gnus-group-make-group). Gnus will prompt you for a name, a method and possibly an address.

比如新建Starred Group G m 之后提示输入Group: Starred, 然后select-method-for-new-group: nnimap:qq.com

Z R 更新 gnus, 然后 L, 便能查看新建的starred Group 设置为可视, G p 之后, 添加一行 (visible . t)

Delete and Rename Group

G <DEL> : gnus-group-delete-group

删除当前的邮件组

b : Find bogus groups and delete them (gnus-group-check-bogus-groups).

删除bogus邮件组

G r : Rename the current group to something else (gnus-group-rename-group). This is valid only on some groups—mail groups mostly. This command might very well be quite slow on some back ends.

Mark Groups

c : Mark all unticked articles in this group as read (gnus-group-catchup-current). gnus-group-catchup-group-hook is called when catching up a group from the group buffer.

C : Mark all articles in this group, even the ticked ones, as read (gnus-group-catchup-current-all).

表组Group内所有邮件为已读

#

:

M m : Set the mark on the current group (gnus-group-mark-group).

M-#

:

M u : Remove the mark from the current group (gnus-group-unmark-group).

M U : Remove the mark from all groups (gnus-group-unmark-all-groups).

M w : Mark all groups between point and mark (gnus-group-mark-region).

M b : Mark all groups in the buffer (gnus-group-mark-buffer).

M r : Mark all groups that match some regular expression (gnus-group-mark-regexp).

Sorting Groups

常用的排序方法:

G S a : Sort the group buffer alphabetically by group name (gnus-group-sort-groups-by-alphabet).

G S m : Sort the group buffer alphabetically by back end name (gnus-group-sort-groups-by-method).

G S n : Sort the group buffer alphabetically by real (unprefixed) group name (gnus-group-sort-groups-by-real-name).

Group Timestamp

Trace最近一次打开group的时间戳.(add-hook 'gnus-select-group-hook 'gnus-group-set-timestamp It can be convenient to let Gnus keep track of when you last read a group. To set the ball rolling, you should add gnus-group-set-timestamp to gnus-select-group-hook: 添加配置:

(add-hook 'gnus-select-group-hook 'gnus-group-set-timestamp)

(setq gnus-group-line-format
      "%M%S%p%P%5y: %(%-40,40g%) %udn")
         (defun gnus-user-format-function-d (headers)
           (let ((time (gnus-group-timestamp gnus-tmp-group)))
             (if time
                 (format-time-string "%b %d  %H:%M" time)
               "")))

Group Highlighting

Gnus Manual中推荐的highlight配色配置,

(cond (window-system
       (setq custom-background-mode 'light)
       (defface my-group-face-1
         '((t (:foreground "Red" :bold t))) "First group face")
       (defface (message "")y-group-face-2
         '((t (:foreground "DarkSeaGreen4" :bold t)))
         "Second group face")
       (defface my-group-face-3
         '((t (:foreground "Green4" :bold t))) "Third group face")
       (defface my-group-face-4
         '((t (:foreground "SteelBlue" :bold t))) "Fourth group face")
       (defface my-group-face-5
         '((t (:foreground "Blue" :bold t))) "Fifth group face")))

(setq gnus-group-highlight
      '(((> unread 200) . my-group-face-1)
        ((and (< level 3) (zerop unread)) . my-group-face-2)
        ((< level 3) . my-group-face-3)
        ((zerop unread) . my-group-face-4)
        (t . my-group-face-5)))

Gnus: 4.Summary Buffer

Display all

Summary Buffer是Email list, “INBOX"默认只读取"未读"邮件, 没有未读邮件则为空, 此默认设置并不实用. 从Group Buffer中的"inbox”,操作 G p, 添加一行 (display . all)

((modseq)
 (uidvalidity .
              #("1579733041" 0 10
                (ws-butler-chg chg)))
 (active 1 . 95)
 (timestamp 24131 49229)
 (display . all) ;; display all emails
 (visible . t)
 (permanent-flags %* %Answered %Flagged %Deleted %Draft %Seen))

Delete Mails

Gnus删除邮件需要格外注意, 首先设置,

;; Delele mail
(setq nnmail-expiry-wait 'never)
(setq nnmail-expiry-target "Deleted Messages")

nnmail-expiry-wait 设置为never, 避免gnus自动删除邮件

删除邮件的动作, 操作移动邮件来完成. Summary-Buffer中操作 B m, 选定待删除的邮件移动到"Deleted Messages"

B m : Move the article from one mail group to another (gnus-summary-move-article). Marks will be preserved if gnus-preserve-marks is non-nil (which is the default).

{width=“600px”}

Search Mails

从Summary Buffer中查找邮件.

M-s Search through all subsequent (raw) articles for a regexp (gnus-summary-search-article-forward).

M-r Search through all previous (raw) articles for a regexp (gnus-summary-search-article-backward).

M-& Perform any operation on all articles that have been marked with the process mark (gnus-summary-universal-argument).

也可以从"Group Buffer"中直接搜索

G G :: nnir is a Gnus interface to a number of tools for searching through mail and news repositories. Different backends (like nnimap and nntp) work with different tools (called engines in nnir lingo), but all use the same basic search interface.

Frequent Commands

回复邮件

  • S r r :: Mail a reply to the author of the current article (gnus-summary-reply).
  • S R R :: Mail a reply to the author of the current article and include the original message (gnus-summary-reply-with-original). This command uses the process/prefix convention
  • S w :: Mail a wide reply to the author of the current article (gnus-summary-wide-reply). A wide reply is a reply that goes out to all people listed in the To, From (or Reply-to) and Cc headers. If Mail-Followup-To is present, that’s used instead.
  • S W :: Mail a wide reply to the current article and include the original message (gnus-summary-wide-reply-with-original). This command uses the process/prefix convention, but only uses the headers from the first article to determine the recipients.
  • S f or f :: Post a followup to the current article (gnus-summary-followup).
  • S F or F :: Post a followup to the current article and include the original message (gnus-summary-followup-with-original). This command uses the process/prefix convention.

写邮件与转发邮件

S p or a : Prepare for posting an article (gnus-summary-post-news). By default, post to the current group. If given a prefix, disable that. If the prefix is 1, prompt for another group instead.

S m or m : Prepare a mail (gnus-summary-mail-other-window). By default, use the posting style of the current group. If given a prefix, disable that. If the prefix is 1, prompt for a group name to find the posting style.

取消发送

S c : Canceling Article Find the article you wish to cancel (you can only cancel your own articles, so don’t try any funny stuff). Then press C or S c (gnus-summary-cancel-article). Your article will be canceled—machines all over the world will be deleting your article. This command uses the process/prefix convention.

延迟发送

C-c C-j

: Delayed Articles

Normally, to send a message you use the C-c C-c command from Message mode. To delay a message, use C-c C-j (`gnus-delay-article`) instead.

Mark Articles

M c or M-u : Clear all readedness-marks from the current article (gnus-summary-clear-mark-forward). In other words, mark the article as unread.

M t or !

: Tick the current article (gnus-summary-tick-article-forward).

M C : Mark all unread articles as read (gnus-summary-catchup).

M C-c : Mark all articles in the group as read—even the ticked and dormant articles (gnus-summary-catchup-all).

Process Mark Process marks are displayed as # in the summary buffer, and are used for marking articles in such a way that other commands will process these articles.

  • M P p

  • M P u or M-# :: Remove the process mark, if any, from the current article (gnus-summary-unmark-as-processable).

M P U : Remove the process mark from all articles (gnus-summary-unmark-all-processable).

Thread Commands

  • T k C-M-k :: Mark all articles in the current (sub-)thread as read (gnus-summary-kill-thread). If the prefix argument is positive, remove all marks instead. If the prefix argument is negative, tick articles instead.

Asynchronous Fetch ;;3.11 Asynchronous Article Fetching (setq gnus-asynchronous t) ;;pre-fetch only unread articles shorter than 100 lines, you could say something like: (defun my-async-short-unread-p (data) “Return non-nil for short, unread articles.” (and (gnus-data-unread-p data) (< (mail-header-lines (gnus-data-header data)) 100))) (setq gnus-async-prefetch-article-p 'my-async-short-unread-p)

Persistent Articles

* : Make the current article persistent (gnus-cache-enter-article).

M-* : Remove the current article from the persistent articles (gnus-cache-remove-article). This will normally delete the article.

(setq gnus-use-cache 'passive)

Article Treatement highlight的部分值得一看, 但是在第三部分article中.

Summary Sorting

You can have the summary buffer sorted in various ways, even though I can’t really see why you’d want that.

C-c C-s C-n : Sort by article number (gnus-summary-sort-by-number).

C-c C-s C-m C-n : Sort by most recent article number (gnus-summary-sort-by-most-recent-number).

C-c C-s C-a : Sort by author (gnus-summary-sort-by-author).

C-c C-s C-t : Sort by recipient (gnus-summary-sort-by-recipient).

C-c C-s C-s : Sort by subject (gnus-summary-sort-by-subject).

C-c C-s C-d : Sort by date (gnus-summary-sort-by-date).

C-c C-s C-m C-d : Sort by most recent date (gnus-summary-sort-by-most-recent-date).

C-c C-s C-l : Sort by lines (gnus-summary-sort-by-lines).

C-c C-s C-c : Sort by article length (gnus-summary-sort-by-chars).

C-c C-s C-m C-m : Sort by article “readedness” marks (gnus-summary-sort-by-marks).

C-c C-s C-i : Sort by score (gnus-summary-sort-by-score).

C-c C-s C-r : Randomize (gnus-summary-sort-by-random).

C-c C-s C-o : Sort using the default sorting method (gnus-summary-sort-by-original).

Finding the Parent

^ : If you’d like to read the parent of the current article, and it is not displayed in the summary buffer, you might still be able to.

Gnus: 5.Article Buffer

"Article Buffer"是邮件的正文内容.

Hiding Headers

(setq gnus-visible-headers "^From:|^Subject:")
(setq gnus-ignored-headers "^References:|^Xref:")
(setq gnus-sorted-header-list '("^From:" "^Subject:"))

Block Ads

(setq gnus-blocked-images "ads")

总结:
Gnus的三个概念: Group Buffer(邮件组), Summary Buffer(邮件列表), Article Buffer(邮件正文)

~/.gnus完整配置

    ;;; Package --- gnus
    ;;;

    (setq user-mail-address "abst.proc.do@qq.com"
          user-full-name "abst.proc.do")

    (setq gnus-select-method
          '(nnimap "qq.com"
                   (nnimap-address "imap.qq.com")
                   (nnimap-inbox "INBOX")
                   (nnimap-split-methods default)
                   (nnimap-expunge t)
                   (nnimap-server-port 993)
                   (nnimap-stream ssl)))

    (setq send-mail-function 'smtpmail-send-it
          smtpmail-smtp-server "smtp.qq.com"
          smtpmail-smtp-service 465
          smtpmail-stream-type 'ssl
          gnus-ignored-newsgroups "^to\\.\\|^[0-9. ]+\\( \\|$\\)\\|^[\"]\"[#'()]")

    ;;(setq gnus-permanently-visible-groups "INBOX")


    ;; Configuration Following Gnus Manual
    ;; 2.1.3 Group Highlighting
    (cond (window-system
           (setq custom-background-mode 'light)
           (defface my-group-face-1
             '((t (:foreground "Red" :bold t))) "First group face")
           (defface my-group-face-2
             '((t (:foreground "DarkSeaGreen4" :bold t)))
             "Second group face")
           (defface my-group-face-3
             '((t (:foreground "Green4" :bold t))) "Third group face")
           (defface my-group-face-4
             '((t (:foreground "SteelBlue" :bold t))) "Fourth group face")
           (defface my-group-face-5
             '((t (:foreground "Blue" :bold t))) "Fifth group face")))

    (setq gnus-group-highlight
          '(((> unread 200) . my-group-face-1)
            ((and (< level 3) (zerop unread)) . my-group-face-2)
            ((< level 3) . my-group-face-3)
            ((zerop unread) . my-group-face-4)
            (t . my-group-face-5)))

    ;;2.18.3 Group Timestamp
    (add-hook 'gnus-select-group-hook 'gnus-group-set-timestamp)
    ;;(setq gnus-group-line-format
    ;;"%M%S%p%P%5y: %(%-40,40g%) %6,6~(cut 2)dn")

    ;; (setq gnus-group-line-format
    ;;       "%M%S%p%P%5y: %(%-40,40g%) %udn")
    ;; (defun gnus-user-format-function-d (headers)
    ;;   (let ((time (gnus-group-timestamp gnus-tmp-group)))
    ;;     (if time
    ;;         (format-time-string "%b %d  %H:%M" time)
    ;;       "")))

    ;;3.6 Delayed Articles
    (gnus-delay-initialize)


    ;;3.10 Sorting the Summary Buffer
    (setq gnus-thread-sort-functions
          '((not gnus-thread-sort-by-number)
            gnus-thread-sort-by-score))

    ;;3.11 Asynchronous Article Fetching
    (setq gnus-asynchronous t)
    ;;pre-fetch only unread articles shorter than 100 lines, you could say something like:
    (defun my-async-short-unread-p (data)
      "Return non-nil for short, unread articles."
      (and (gnus-data-unread-p data)
           (< (mail-header-lines (gnus-data-header data))
              100)))
    (setq gnus-async-prefetch-article-p 'my-async-short-unread-p)

    ;;3.13 Persistent Articles
    (setq gnus-use-cache 'passive)

    ;; 3.25 Tree Display
    (setq gnus-use-trees nil)
    ;; (setq gnus-use-trees t
    ;;       gnus-generate-tree-function 'gnus-generate-horizontal-tree
    ;;       gnus-tree-minimize-window nil)
    ;; (gnus-add-configuration
    ;;  '(article
    ;;    (vertical 1.0
    ;;              (horizontal 0.25
    ;;                          (summary 0.75 point)
    ;;                          (tree 1.0))
    ;;              (article 1.0))))


    ;;4.3 HTML
    (setq gnus-blocked-images "ads")

    ;;5.4 Mail and Post
    (add-hook 'message-send-hook 'ispell-message)

    ;;5.5 Archived Messages
    (setq gnus-message-archive-group nil)


    ;;6.4.9 Expiring Mail
    (remove-hook 'gnus-mark-article-hook              'gnus-summary-mark-read-and-unread-as-read)
    (add-hook 'gnus-mark-article-hook 'gnus-summary-mark-unread-as-read)
    ;; Delele mail
    (setq nnmail-expiry-wait 'never)
    (setq nnmail-expiry-target "Deleted Messages")

    ;;9.5 Window Layout
    (setq gnus-use-full-window nil)
    ;;((group (vertical 1.0 (group 1.0 point)))
    ;; (article (vertical 1.0 (summary 0.25 point)
    ;;                    (article 1.0))))

    ;; (gnus-configure-frame
    ;;  '(frame 1.0
    ;;          (vertical 1.0
    ;;                    (summary 0.25 point frame-focus)
    ;;                    (article 1.0))
    ;;          (vertical ((height . 5) (width . 15)
    ;;                     (user-position . t)
    ;;                     (left . -1) (top . 1))
    ;;                    (picon 1.0))))

上传了org版本的Gnus-manual 20200213-184926

Appendix D也上传

Gnus Easter Eggs

读Gnus文档枯燥无味, 摘取其中有趣有料的句子.

3.12 Article Caching

  • As you may ~surmise~, this could potentially use /huge/ amounts of disk space, as well as eat up all your inodes so fast it will make your head ~swim in vodka~.

3.14 Sticky Articles

  • But sometimes you might want to display all the latest emails from your mother, your father, your aunt, your uncle and ~your 17 cousins~ to coordinate the next Christmas party

5.2 Posting Server

  • Thank you for asking. I hate you.

5.7 Drafts

  • If you are writing a message (mail or news) and suddenly remember that you have a ~steak in the oven~ (or some pesto in the food processor, you craaazy vegetarians), you’ll probably wish there was a method to save the message you are writing so that you can continue editing it some other day, and send it when you feel its finished.

6.4.9 Expiring Mail

  • If you are writing a message (mail or news) and suddenly remember that you have a steak in the oven (or some pesto in the food processor, you craaazy vegetarians), you’ll probably wish there was a method to save the message you are writing so that you can continue editing it some other day, and send it when you feel its finished.

最出彩的是"Sending Mail"的一段:

32.1 The Format of the Mail Buffer

#+BEGIN_EXAMPLE
To: subotai@example.org
Cc: mongol.soldier@example.net, rms@gnu.org
Subject: Re: What is best in life?
From: conan@example.org
--text follows this line--
To crush your enemies, see them driven before you, and to
hear the ~lamentation~ of their women.
#+END_EXAMPLE