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 "[email protected]")
(setq user-full-name "abst.proc.do")

(setq smtpmail-smtp-user "[email protected]"
      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 [email protected] 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 "[email protected]")
(setq user-full-name "abst.proc.do")

(setq smtpmail-smtp-user "[email protected]"
      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: '[email protected] (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

10 个赞

Gnus: 1.Fundermental Configuration

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

(setq user-mail-address "[email protected]"
      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 [email protected] password 授权码
machine smtp.qq.com  login [email protected] 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)))
1 个赞

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 "[email protected]"
          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))))
1 个赞

上传了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: [email protected]
Cc: [email protected], [email protected]
Subject: Re: What is best in life?
From: [email protected]
--text follows this line--
To crush your enemies, see them driven before you, and to
hear the ~lamentation~ of their women.
#+END_EXAMPLE
2 个赞

在gnus里,如果有两个邮件账号,怎么设置smtpmail的参数来保证邮件可以正确从不同的发送服务器发出呢?比如一个gmail,一个hotmail? 谢谢

如果你用 emacs29, 可以试试 message-server-alist

;; Message 自动设置 "X-Message-SMTP-Method" 功能。
(defun eh-message-server-alist-function ()
  (let* ((from (cadr (mail-extract-address-components
                      (save-restriction
                        (widen)
                        (message-narrow-to-headers-or-head)
                        (message-fetch-field "From")))))
         (str (replace-regexp-in-string "^.*@" "" from)))
    (when (> (length str) 0)
      (format "smtp smtp.%s 465 %s" str from))))

(setq message-server-alist '((eh-message-server-alist-function)))

以前我是用包 gnus-select-account, 两种方法都是自动设置:X-Message-SMTP-Method

Gnus特别困难,但是我已经花了太多功夫。 传统方法是配置msmtp。 可是从Group buffer还需要映射击键"m" (gnus-group-mail)到预定的发送服务器。 Gnus具有隐藏功能叫gnus-posting-styles:

(setq gnus-posting-styles
      '(("^nnimap.*${GMAIL_USER}"
         (name "${GMAIL_USER}")
         (address "${GMAIL_USER}@gmail.com"))
        ("^nnimap.*${HOTMAIL_USER}"
         (name "${HOTMAIL_USER}")
         (address "${HOTMAIL_USER}@hotmail.com"))))

完整的配置: GitHub - dickmao/gnus-imap-walkthrough: Provably set up Gnus for IMAP accounts from scratch.

1 个赞

找到一篇文章,解决问题

借这个帖子,分享下最近几天折腾gnus 的经验 先说多账号发邮件: 综合上面几个帖子 总结了以下内容

根据from: 自动设置 “X-Message-SMTP-Method” 功能。 也就是说根据发件人不同自动去 .authinfo 中寻找相应的smtp 信息用于填充 header “X-Message-SMTP-Method”

当header中已经有X-Message-SMTP-Method header时将直接使用header中的值

需要 .authinfo .authinfo.gpg 中有以下格式的配置

machine smtp.qq.com port 465 login [email protected]  protocol smtp  password yourpasswd

(defun vmacs-message-server-alist-function ()
  "guess smpt server by From: header"
  (let* ((from (cadr (mail-extract-address-components
                      (save-restriction
                        (widen)
                        (message-narrow-to-headers-or-head)
                        (message-fetch-field "From")))))
         (auth (auth-source-search :user from :protocol "smtp" :require '(:host))))
    (when auth
      (let* ((entry (car auth))
             (host (plist-get entry :host))
             (port (plist-get entry :port)))
        (format "smtp %s %s %s" host (or port "465") from)))))
(setq message-server-alist '((vmacs-message-server-alist-function)))

2. 回复邮件时自动设置X-Message-SMTP-Method

就是上面帖子说的 gnus-posting-styles 这个变量相关的 匹配的时候 也支持(header "to" ,user-mail-address-3)这种格式 根据to的地址来设定回复邮件时的X-Message-SMTP-Method

(setq gnus-posting-styles               ;C-h S check info of gnus-posting-styles
      `((".*" ; Matches all groups of messages with default qq
         (address (concat (format "%s <%s>" ,user-full-name ,user-mail-address) ))
         ("X-Message-SMTP-Method" (concat "smtp smtp.qq.com 465 " ,user-mail-address)))
        ((header "to" ,user-mail-address-2)
         (address ,user-mail-address-2)
         ("X-Message-SMTP-Method" (concat "smtp smtp.qq.com 465 " ,user-mail-address-2)))
        ((header "to" ,user-mail-address-3)
         (address ,user-mail-address-3)
         ("X-Message-SMTP-Method" (concat "smtp smtp.gmail.com 465 " ,user-mail-address-3)))))

3. 在编辑邮件时,定制在From: 之后按下tab 的行为(切换我的几个邮箱地址)

(with-eval-after-load 'message
  ;; From: 后 tab 键 切换 发送邮箱
  (add-to-list 'message-completion-alist (cons "^\\([^ :]*-\\)?\\(From\\):" #'message-toggle-from)))

;; user-work-mail-address user-mail-address-2 等变量为我其他两个邮箱地址
(defun message-toggle-from()
  "Toggle addresses in the From: field of the message buffer."
  (interactive)
  (save-excursion
    (message-goto-from)
    (cond
     ((string-match (concat "^from:\s-*.*" user-mail-address) (thing-at-point 'line))
	  (beginning-of-line)
	  (message-delete-line)
      (insert (concat "From: " user-full-name " <" user-work-mail-address ">\n")))
     ((string-match (concat "^from:\s-*.*" user-work-mail-address) (thing-at-point 'line))
	  (beginning-of-line)
	  (message-delete-line)
      (insert (concat "From: " user-mail-address-2 "\n")))
     (t
	  (beginning-of-line)
	  (message-delete-line)
	  (insert (concat "From: " user-full-name " <" user-mail-address ">\n"))))))

再说收邮件, 目前我采用的方案是 mbsync 同步邮件到本地

使用notmuch 对邮件进行索引,gnus使用notmuch的搜索功能

从而利用gnus 的nnselect+gnus-search-notmuch功能对邮件进行分组 可以实现以下分组:

  1. unread (需要利用 notmuch 的tag)
  2. 根据cc to bcc,List-Id 等header 对mail list 的邮件进行分组 这种方式 因邮件在本地,notmuch索引搜索相对imap的联网操作较快

我的多个邮箱 通过自动转发功能 汇总到一个邮箱,mbsync同步时只同步这一个,速度会快一些

这是我的mbsyncrc的配置

IMAPAccount qq
Host imap.qq.com
Port 993
User [email protected]
# 从 keychain 获取密码 https://www.jianshu.com/p/191978f84853
# security add-internet-password -a "所属用户" -s "密码项名称" -w "密码"
# security add-internet-password -a "[email protected]" -s "smtp.exmail.qq.com" -w "mypass"
PassCmd "gpg -q --for-your-eyes-only --no-tty -d ~/.authinfo.gpg 2>/dev/null |grep imap.qq.com|grep [email protected]| awk '/password/ {print $NF}'"
# PassCmd "security find-internet-password -g -a [email protected] -s smtp.qq.com -w"
SSLType IMAPS
SSLVersions TLSv1.2

IMAPStore qq-remote
Account qq

# This section describes the local storage
MaildirStore qq-local
Path ~/maildir/qq/
Inbox ~/maildir/qq/inbox
Flatten .
# The SubFolders option allows to represent all
# IMAP subfolders as local subfolders
SubFolders Verbatim

# This section a "channel", a connection between remote and local
Channel qq
Far :qq-remote:
Near :qq-local:
Patterns *
Expunge Both
CopyArrivalDate yes
Sync All
Create Near
SyncState *

定时同步mbsync 的脚本如下: mbsync.timer

[Unit]
Description=Mysync synchronization timer

[Timer]
OnBootSec=2m
OnUnitActiveSec=20m
Unit=mbsync.service

[Install]
WantedBy=timers.target

mbsync.service

[Unit]
Description=Mbsync synchronization service

[Service]
#cjk 
Environment="XAPIAN_CJK_NGRAM=1"
Type=oneshot
ExecStart=/usr/bin/mbsync -Va
ExecStartPost=/usr/bin/sh -c '/usr/bin/notmuch new;/usr/local/bin/mbsync-notify'
[Install]
WantedBy=default.target

当同步时如有新邮件 通过mbsync-notify 脚本来通知

#!/usr/bin/bash
#
# Script to notify user for new mails.
# Crontab ex:
# */3 * * * * $HOME/.local/scripts/mbsync-notify.sh [acc_name]
#

# do not duplicate
killall mbsync &>/dev/null

#run mbsync once for all accs with named configs file, with quiet interface
# or specify account name as argument and define separate cron tasks for each acc
if [ -n $1 ]
then
  acc=$1
else
  acc=
fi
/usr/bin/mbsync  -a $acc -q 2>/dev/null

#count new mail for every maildir, only in INBOX
# since there are maildirs/accounts in a format `~/Mail/account1/subacc1`,
# `~/Mail/account1/subacc2`, `~/Mail/account2/subacc1` etc
notification=""
# 遍历每个maildir目录
for maildir in "$HOME/maildir/"*/; do
  inbox_dir="${maildir}inbox"
  if [ -d "$inbox_dir" ]; then
      new_count=$(find "${maildir}inbox/new/" -type f | wc -l)
      old_count=$(find "${maildir}inbox/cur/" -type f | wc -l)
      if [ "$new_count" -gt 0 ]; then
          if [ "$notification" = "" ]; then
              notification+="目录: $(basename "$maildir") 新: $new_count 旧: $old_count"
          else
              notification+="\n目录: $(basename "$maildir") 新: $new_count 旧: $old_count"
          fi
      fi
  fi
done

# 如果有新邮件,发送一次通知
if [ -n "$notification" ]; then
  /usr/bin/notify-send -a "Mbsync" "✉ 新邮件!" "${notification}"
fi

退出gnus时,或手动进行同步的命令如下,同步之后会刷新group或summary buffer:

  (define-key gnus-summary-mode-map (kbd "C-c Gu") #'mbsync)                 ;gu
  (define-key gnus-group-mode-map (kbd "C-c Gu") #'mbsync)                 ;gu

(setq gnus-after-exiting-gnus-hook #'mbsync)

(defun mbsync()
  (interactive)
  (let ((process (start-process "mbsync" "*Messages*" "sh" "-c" "mbsync -aq;notmuch new")))
    (set-process-query-on-exit-flag process nil)
    (set-process-sentinel
     process
     (lambda (proc _)
       (when (eq (process-status proc) 'exit)
         (when (eq major-mode 'gnus-group-mode)
           (gnus-group-get-new-news))
         (when (eq major-mode 'gnus-summary-mode)
           (gnus-summary-rescan-group)))))))

notmuch的配置文件.notmuch-config也很简单

# .notmuch-config - Configuration file for the notmuch mail system
#
# For more information about notmuch, see https://notmuchmail.org
# Database configuration
#
# The only value supported here is 'path' which should be the top-level
# directory where your mail currently exists and to where mail will be
# delivered in the future. Files should be individual email messages.
# Notmuch will store its database within a sub-directory of the path
# configured here named ".notmuch".
#
[database]
path=/home/user/maildir
# User configuration
#
# Here is where you can let notmuch know how you would like to be
# addressed. Valid settings are
#
#	name		Your full name.
#	primary_email	Your primary email address.
#	other_email	A list (separated by ';') of other email addresses
#			at which you receive email.
#
# Notmuch will use the various email addresses configured here when
# formatting replies. It will avoid including your own addresses in the
# recipient list of replies, and will set the From address based on the
# address to which the original email was addressed.
#
[user]
name=username1
[email protected]
[email protected]
# Configuration for "notmuch new"
#
# The following options are supported here:
#
#	tags	A list (separated by ';') of the tags that will be
#		added to all messages incorporated by "notmuch new".
#
#	ignore	A list (separated by ';') of file and directory names
#		that will not be searched for messages by "notmuch new".
#
#		NOTE: *Every* file/directory that goes by one of those
#		names will be ignored, independent of its depth/location
#		in the mail store.
#
[new]
tags=unread;inbox;
ignore=.uidvalidity;.mbsyncstate.new;.mbsyncstate.journal;.mbsyncstate;slrnpull.conf;pass;.nnmaildir

# Search configuration
#
# The following option is supported here:
#
#	exclude_tags
#		A ;-separated list of tags that will be excluded from
#		search results by default.  Using an excluded tag in a
#		query will override that exclusion.
#
[search]
# Maildir compatibility configuration
#
# The following option is supported here:
#
#	synchronize_flags      Valid values are true and false.
#
#	If true, then the following maildir flags (in message filenames)
#	will be synchronized with the corresponding notmuch tags:
#
#		Flag	Tag
#		----	-------
#		D	draft
#		F	flagged
#		P	passed
#		R	replied
#		S	unread (added when 'S' flag is not present)
#
#	The "notmuch new" command will notice flag changes in filenames
#	and update tags, while the "notmuch tag" and "notmuch restore"
#	commands will notice tag changes and update flags in filenames
#
[index]
# 支持的搜索自定义header
# https://stackoverflow.com/questions/37480617/search-for-custom-header-value-in-notmuch
# after change config run:  notmuch reindex '*'
# then search with: notmuch search ListId:emacs-devel
header.List=List-Id

[maildir]
synchronize_flags=true

#  Local Variables:
#  mode: conf
#  eval: (add-hook (make-local-variable 'after-save-hook) #'(lambda()(shell-command "gpg -d notmuch-config.gpg>notmuch-config")) t)
#  End:

先分享一下 效果图

1 个赞

这是收邮件相关的配置, 里面注释相对较全, 不全的也可以看相应变量的注释,就不多做解释了。


(setq gnus-select-method '(nnnil ""))
(setq gnus-secondary-select-methods
      `((nnmaildir ,user-full-name
                   (directory "~/maildir/mail1")
                   (gnus-search-engine gnus-search-notmuch
                                       (remove-prefix ,(expand-file-name"~/maildir/mail1"))
                                       (config-file ,(expand-file-name "~/.notmuch-config"))))
        (nnmaildir "mail2"  (directory "~/maildir/mail2")
                   (gnus-search-engine gnus-search-notmuch
                                       (remove-prefix ,(expand-file-name"~/maildir/mail2"))
                                       (config-file ,(expand-file-name "~/.notmuch-config"))))))
;; https://www.gnu.org/software/emacs/manual/html_node/gnus/Group-Parameters.html
(setq gnus-parameters
      `(("nnvirtual:.*"; 暂时没用到,可以将两个inbox 的聚合到一个group下
         (gnus-show-threads t)
         (gnus-article-sort-functions '((not gnus-article-sort-by-number))) ;not 是倒序的意思
         (gnus-use-scoring nil)
         (display . 500))
        ("nnselect:.*"
         ;; https://www.gnu.org/software/emacs/manual/html_node/gnus/Selection-Groups.html
         ;; 如果不加(nnselect-rescan t)
         ;; g: gnus-group-get-new-news 的时候 并不会重新搜索以刷新nnselect 的group内容
         ;; 但我的nnselect 是notmuch本地搜索返回的结果相对较快,故打开此开关
         (nnselect-rescan t)
         ;; 默认情况下newsrc 会缓存搜索的结果(nnselect-always-regenerate t) 后则不缓存
         ;; 每次都重新生成
         ;; (nnselect-always-regenerate t)
         (gnus-show-threads t)
         ;; C-c C-s C-a 排序 author, C-c C-s C-d:date
         ;; 排序越靠后 优先级越高
         (gnus-thread-sort-functions '(gnus-thread-sort-by-most-recent-date
                                       (not gnus-thread-sort-by-date)
                                     ))
         ;; (gnus-article-sort-functions '((not gnus-article-sort-by-date))) ;not 是倒序的意思
         (gnus-use-scoring nil)
         (display . all))
        (,(format "nnmaildir.*%s:.*" user-full-name)
         (gnus-show-threads nil)
         (gnus-article-sort-functions '((not gnus-article-sort-by-date))) ;not 是倒序的意思
         ;;expiry-wait expire-group 对gnus-secondary-select-methods中配的maildir 似乎未生效
         ;; (expire-group .  "nnmaildir+jixiuf:Deleted Messages")
         ;; (expiry-wait . immediate)              ;E的邮件,多久后真正删除 see nnmail-expiry-wait
         (gnus-use-scoring nil)
         (display . 500)  ;C-u ret 可指定别的数量big enouch without confirm
         ;; (display . all)
         )
        ("inbox"
         ;; "nnmaildir.*qq:.*"中配置的expiry-wait expire-group
         ;; 对gnus-secondary-select-methods中配的maildir 似乎未生效
         ;; 比如说我的group是nnmaildir+qq:inbox 它应该匹配gnus-parameters中"nnmaildir.*qq:.*"
         ;; 中配置的parameters,但并没有,所以我下面又单独加了 "inbox",Sent Messages等的规则
         ;; 删除后移动哪个组  nnmail-expiry-wait  nnmail-expiry-target
         ;; 也可以使用Bm 移动邮件的操作,来实现挪到“已删除邮件箱的功能”
         (expiry-wait . immediate)              ;E的邮件,多久后真正删除 see nnmail-expiry-wait
         ;;  ;;; 删除后移动哪个组 nnmail-expiry-target
         (expire-group .  ,(format "nnmaildir+%s:Deleted Messages" user-full-name))
         )
        ("Sent Messages\\|Drafts"
         (gnus-show-threads nil)
         (gnus-article-sort-functions '((not gnus-article-sort-by-number))) ;not 是倒序的意思
         (expiry-wait . immediate)              ;E的邮件,多久后真正删除 see nnmail-expiry-wait
         (expire-group . ,(format "nnmaildir+%s:Deleted Messages" user-full-name))
         (display . 500)  ;C-u ret 可指定别的数量big enouch without confirm
         (gnus-use-scoring nil))
        ("Deleted Messages\\|Junk"
         (gnus-show-threads nil)
         (gnus-article-sort-functions '((not gnus-article-sort-by-date))) ;not 是倒序的意思
         (expiry-wait . immediate)              ;E的邮件,多久后真正删除 see nnmail-expiry-wait
         (display . 500)  ;C-u ret 可指定别的数量big enouch without confirm
         (gnus-use-scoring nil))))
;; (setq mm-discouraged-alternatives '( "text/html" "text/richtext"))
(when window-system
  (setq gnus-sum-thread-tree-indent " ")
  (setq gnus-sum-thread-tree-root "") ;; "● ")
  (setq gnus-sum-thread-tree-false-root "") ;; "◯ ")
  (setq gnus-sum-thread-tree-single-indent "") ;; "◎ ")
  (setq gnus-sum-thread-tree-vertical        "│")
  (setq gnus-sum-thread-tree-leaf-with-other "├─► ")
  (setq gnus-sum-thread-tree-single-leaf     "╰─► "))
(setq gnus-use-dribble-file nil)
(setq gnus-always-read-dribble-file nil)
(setq gnus-save-newsrc-file nil)
(setq gnus-read-newsrc-file nil)
(setq gnus-fetch-old-headers t)
(setq gnus-article-date-headers '(local))
;; 默认展示article 上,最上方25%为summary 所占,
;; M-x:gnus-summary-select-article-buffer (default:h) 此选项则仅展示article
;; summary buffer 中 s/h 是一对命令,h 相当于hide summary,s 则show summary
;; 会根据gnus-widen-article-window 的值 来决定 summary buffer是否展示
(setq gnus-widen-article-window t)
(setq gnus-single-article-buffer t)
;; default 200, stop gnus to ask me "how many articles from"
(setq gnus-large-newsgroup nil)
(setq gnus-visual-mark-article-hook nil) ;下面使用hl-line-mode 来替代此功能
(setq gnus-summary-mode-hook 'hl-line-mode)
(setq gnus-group-mode-hook  '(gnus-topic-mode hl-line-mode))
;; (setq gnus-message-archive-method '(nnmaildir "archive" (directory "~/maildir/archive")))
;; gnus-level-subscribed:5
(setq gnus-group-line-format "Lv%L\ %M\ %S\ %p\ %P\ %5y/%-5t:%B%(%g%)\n")
;; https://www.math.utah.edu/docs/info/gnus_5.html#SEC51
(setq gnus-summary-line-format
      (concat
       "%0{%U%R%z%}"
       ;; "%3{│%}" "%1{%d%}" "%3{│%}" ;; date
       "%3{│%}" "%3{%-18,18&user-date;%}" "%3{│%}" ;; date
       ""
       "%4{%-12,12f%}"               ;; name
       " "
       "%3{│%}"
       " "
       "%1{%B%}"
       "%S\n"))
;; (setq gnus-activate-level 4)
;; gnus-summary-line-format 内通过%&user-date; 自定义时间格式
(setq gnus-user-date-format-alist (quote (
					                      ((gnus-seconds-today) . "%a%b%d %H:%M今")
					                      ((+ 86400 (gnus-seconds-today)) . "%a%b%d %H:%M昨")
					                      ((gnus-seconds-year) . "%a%b%d %H:%M")
					                      (t . "%a%y年%b%d %H:%M"))))
(setq gnus-permanently-visible-groups;不管有没有未读,都展示
      "qq$\\|gmail$\\|emacs$\\|inbox$")
;; Gnus的默认配置, 生成 "sent.%Y-%m" 格式的 Send-Mail存档, 这与imap的Send-Messages重复, 因此关闭改功能
(setq gnus-message-archive-group nil)
(setq gnus-interactive-exit nil)     ;退出时不必确认
(setq gnus-expert-user t)
;; GG 搜索支持 subject:keyword from:keyword body:keyword cc:keyword tag:notmuch
;; 可以 #选多个group 进行搜索
;; 搜索后可以 C-cC-p 将这个搜索结果保存为一个nnselect:组
;; Gg 则后将搜索结果保存为一个virtual group
;; 另外可以 GV 创建空的virtuall group ,然后 Gv 将其他group加入到那个空组
;; 如将所有邮箱的inbox 加到这个虚组中
;; https://www.gnu.org/software/emacs/manual/html_node/gnus/Search-Queries.html
(setq gnus-search-use-parsed-queries t) ;GG search group, and / :  limit in summary buffer

;; nndraft 会导致在group 上GG搜索时失败,
;; 我不想显示这个nndraft 目前没找到办法,可以使用u subscribed
;; https://www.gnu.org/software/emacs/manual/html_node/gnus/Drafts.html
;; (with-eval-after-load 'gnus-search ; 这段没生效
;;   (add-to-list 'gnus-search-default-engines '((nndraft . gnus-search-notmuch))))

(delete 'gnus-topic-alist gnus-variable-list)
(delete 'gnus-topic-topology gnus-variable-list)
(setq gnus-topic-topology `(("Gnus" visible)
                            (("misc" visible))
                            ((,user-full-name visible))))
(setq gnus-topic-alist
      `((,user-full-name ; the key of topic
         ,(format "nnmaildir+%s:inbox" user-full-name)
         ,(format "nnmaildir+%s:Deleted Messages" user-full-name)
         ,(format "nnmaildir+%s:Junk" user-full-name)
         ,(format "nnmaildir+%s:Drafts" user-full-name)
         ,(format "nnmaildir+%s:Sent Messages" user-full-name))
        ("misc" ; the key of topic
         ;; 我不想显示这个nndraft 目前没找到办法,可以使用u unsubscribed
         ;; https://www.gnu.org/software/emacs/manual/html_node/gnus/Drafts.html
         "nndraft:drafts"
         ;; "nnfolder+archive:sent.2024-12" // imap server 端会有发件箱,用不到sent了
         )
        ("Gnus"
         ;; https://www.gnu.org/software/emacs/manual/html_node/gnus/Selection-Groups.html
         ;;"nnselect:unread"    通过 Gg 后输入groupname:unread,然后用 tag:unread 作关键词搜索后的结果
         ;; (query . "tag:unread")
         ;; 创建完"nnselect:unread" 后需要通过 Gp 编辑这个group ,在(nnselect-specs 的上一行添加
         ;; (nnselect-rescan t)  (nnselect-always-regenerate t)
         ;; 确保重新进入会刷新
         ;; 保存后,然后重新打开gnus 能其将这段配置写入 .newsrc.eld 文件后即可
         ;; 这个搜索需要信赖gnus-search-notmuch 的支持 notmuch 的配置文件中
         ;; 我有配unread 这个tag ,即新邮件会打上unread的tag
         ;; [new]
         ;; tags=unread;inbox;
         "nnselect:unread"
         ;; 通过 GV 创建"nnvirtual:inbox",后 再通过 Gv 依次将各邮箱的inbox 加入到这个virtual group 后
         ;; 目前只用到一个邮箱,暂时用不上 ,nnvirtual的另一个缺点上 不能在其上继续使用GG  进行搜索
         "nnvirtual:inbox"
         ;; like "nnselect:unread" but with
         ;; address: 发件人+收件人 包括密送 抄送等,
         ;; recipient: 只包括收件人+密送 抄送等
         ;; (query . "address:[email protected] or address:debbugs.gnu.org")
         ;; (query . "recipient:[email protected]")
         ;;gnus 的语法 https://www.gnu.org/software/emacs/manual/html_node/gnus/Search-Queries.html
         ;; notmuch 的语法 https://notmuchmail.org/doc/latest/man7/notmuch-search-terms.html#notmuch-search-terms-7
         ;; notmuch 会使用如下命令查询,因duplicate 去重,若自己给自己发邮件,发件箱也会存一份完全一样的,
         ;; 去重后导致只返回发件箱的,但过滤条件就会再次过滤只显示发件箱就会导致少显示几条
         ;; notmuch  search --output=files --duplicate=1 "to:[email protected] or  or to:[email protected]"
         "nnselect:emacs"
         "nnselect:emacs-info"
         "nnselect:qq"
         "nnselect:feed"
         "nnselect:gmail"
         )))

(setq gnus-visible-headers
       '("^From:" "^To:" "^Cc:" "^Subject:" "^Newsgroups:" "^Date:"
         "Followup-To:" "Reply-To:" "^Organization:" "^X-Newsreader:" "^List-Id"
         "^Sender" "^X-Mailer:"))        ;T 查看article所有header
;; (setq gnus-article-time-format "%F %Y") ; 未生效?

这一段是根据搜索的结果,将其保存为一个group 的相关代码,比如上面截图中的 nnselect:unread

等的实现

(defun gnus-query(args)
  (gnus-search-run-query
   (list
    `(search-query-spec
      (query . ,(car args))
      (raw))
    `(search-group-spec (,(format "nnmaildir:%s" user-full-name)
                         ,(format "nnmaildir+%s:inbox" user-full-name))))))
(add-hook 'gnus-group-mode-hook #'init-my-gnus-group)
(defun init-my-gnus-group()
  ;; 这段代码是将以下手工创建group 的操作固化,以便我换电脑的时候
  ;; 不用再需要重新创建,而是通过代码自动化了
  ;;  下面用到的 user-mail-address-3 是我的gamil邮箱地址,没在此文件中定义
  ;; 如果是手工创建nnselect 组:流程如下:
  ;; 在groupbuffer中 将光标移动到你要搜索的那个group上 或用 # 选多个组
  ;; 然后 Gg 后输入groupname: gmail ,然后输入搜索用的关键字: recipient:yourmailaddress
  ;; gnus 的搜索语法 https://www.gnu.org/software/emacs/manual/html_node/gnus/Search-Queries.html
  ;; 我用到的搜索实现是 gnus-search-notmuch
  ;; 此时刷新group 就会出现一个名为 nnselect:gmail 的新组
  ;; 然后光标移动到这个组上 按下GE 编辑这个组 在 nnselect-specs的上一行
  ;; 添加   (nnselect-rescan t) (nnselect-always-regenerate t)
  ;; 只有这样 下次再进入这个组的时候 才会重新搜索,否则这个组就只是一个快照
  ;; 当然也可以通过 gnus-parameters 为这个组设置 这两个属性
  ;; 我的这个函数 就是将 GE编辑时的部分内容 copy出来通过gnus-group-make-group 实现的
  ;; 编写过程中如果有部Kg 可以通过C-k 删掉某group,以便重建

  ;; 未读邮件单独一个分组 nnselect:unread
  (unless (gnus-group-entry "nnselect:unread")
    (gnus-group-make-group "unread"
                           (list 'nnselect "nnselect")
                           nil
                           (list
                            ;; 这个搜索需要依赖 gnus-search-notmuch 的支持 notmuch 的配置文件中
                            ;; 我有配unread 这个tag ,即新邮件会打上unread的tag
                            ;; [new]
                            ;; tags=unread;inbox;
                            `(nnselect-specs (nnselect-function . gnus-query)
                                             (nnselect-args . ("thread:* tag:unread")))
                            '(nnselect-rescan t)
                            '(nnselect-always-regenerate t)
                            (cons 'nnselect-artlist nil))))

  (unless (gnus-group-entry "nnselect:gmail")
    (gnus-group-make-group
     "gmail"
     (list 'nnselect "nnselect")
     nil
     (list
      `(nnselect-specs (nnselect-function . gnus-search-run-query)
                       (nnselect-args (search-query-spec (query . ,(format "recipient:%s" user-mail-address-3))
                                                         (raw))
                                      (search-group-spec (,(format "nnmaildir:%s" user-full-name)
                                                          ,(format "nnmaildir+%s:inbox" user-full-name)))))
      '(nnselect-rescan t)
      '(nnselect-always-regenerate t)
      (cons 'nnselect-artlist nil))))

  ;; 下面是创建 nnselect:emacs 这个emacs相关邮件定阅分组
  (unless (gnus-group-entry "nnselect:emacs")
    (gnus-group-make-group
     "emacs"
     (list 'nnselect "nnselect")
     nil
     (list
      `(nnselect-specs (nnselect-function . gnus-search-run-query)
                       (nnselect-args
                        (search-query-spec
                         ;;(raw . t) 或者在query 中加上 raw:* 使用notmuch 的原生语法搜索,注 需要在 .notmuch-config 中加入以下内容 ,
                         ;; see gnus-search-use-parsed-queries
                         ;; and https://www.gnu.org/software/emacs/manual/html_node/gnus/Search-Queries.html
                         ;; notmuch raw原生语法 https://notmuchmail.org/doc/latest/man7/notmuch-search-terms.html#notmuch-search-terms-7
                         (raw . t)
                         (query
                          ;; 如 raw:* (List:bug-gnu-emacs.gnu.org or List:emacs-devel.gnu.org or List:[email protected] or List:[email protected]) 
                          ;; 用于支持搜索自定义header:此处为List
                          ;; 可在article中 按t 查看所有header
                          ;; [index]
                          ;; # 支持的搜索自定义header
                          ;; # https://stackoverflow.com/questions/37480617/search-for-custom-header-value-in-notmuch
                          ;; # after change config run:  notmuch reindex '*'
                          ;; # then search with: notmuch search List:emacs-devel.gnu.org
                          ;; header.List=List-Id
                          . "(List:bug-gnu-emacs.gnu.org or List:emacs-devel.gnu.org or List:[email protected] or List:[email protected]) ")
                         )
                        (search-group-spec (,(format "nnmaildir:%s" user-full-name)
                                            ,(format "nnmaildir+%s:inbox" user-full-name)))))
      '(nnselect-rescan t)
      ;; '(nnselect-always-regenerate t)
      (cons 'nnselect-artlist nil))))

  (unless (gnus-group-entry "nnselect:emacs-info")
    (gnus-group-make-group
     "emacs-info"
     (list 'nnselect "nnselect")
     nil
     (list
      `(nnselect-specs (nnselect-function . gnus-search-run-query)
                       (nnselect-args
                        (search-query-spec
                         (query
                          . "recipient:[email protected] or recipient:[email protected]")
                         (raw))
                        (search-group-spec (,(format "nnmaildir:%s" user-full-name)
                                            ,(format "nnmaildir+%s:inbox" user-full-name)))))
      '(nnselect-rescan t)
      ;; '(nnselect-always-regenerate t)
      (cons 'nnselect-artlist nil))))

  (unless (gnus-group-entry "nnselect:feed")
    (gnus-group-make-group
     "feed"
     (list 'nnselect "nnselect")
     nil
     (list
      `(nnselect-specs (nnselect-function . gnus-search-run-query)
                       (nnselect-args
                        (search-query-spec
                         (query
                          . "from:.*@quoramail.com or from:.*@quora.com")
                         (raw))
                        (search-group-spec (,(format "nnmaildir:%s" user-full-name)
                                            ,(format "nnmaildir+%s:inbox" user-full-name)))))
      '(nnselect-rescan t)
      ;; '(nnselect-always-regenerate t)
      (cons 'nnselect-artlist nil))))

  ;; 直发我个人邮箱的分组 nnselect:qq
  (unless (gnus-group-entry "nnselect:qq")
    (gnus-group-make-group
     "qq"
     (list 'nnselect "nnselect")
     nil
     (list
      `(nnselect-specs (nnselect-function . gnus-search-run-query)
                       (nnselect-args
                        (search-query-spec
                         (query . ,qq-mail-query) ;如 recipient:[email protected]
                         (raw))
                        (search-group-spec (,(format "nnmaildir:%s" user-full-name)
                                            ,(format "nnmaildir+%s:inbox" user-full-name)))))
      '(nnselect-rescan t)
      ;; '(nnselect-always-regenerate t)
      (cons 'nnselect-artlist nil))))
  )

已读未读 进行区分相关的几个face:

(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(gnus-summary-normal-ancient ((t (:extend t :foreground "gray"))))
 '(gnus-summary-normal-read ((t (:inherit italic :extend t :foreground "gray58"))))
)

另外附上 我的配置的链接 vmacs/config/emacs/gnus.el at master · jixiuf/vmacs · GitHub

3 个赞

最近可以用 gnus 收取 outlook 的邮件吗?

没有试过 不了解,

之前配置好的,qq 能用,outlook 最近不能用了。