Emacs 系列——使用 Emacs 收发邮件
1 邮件发送
在 Emacs 中发邮件主要通过 message-mode 和相应的 SMTP 配置来实现。message-mode 是 Emacs 内置的邮件撰写模式,提供基本的邮件编辑功能。smtpmail 负责通过 SMTP 协议实际发送邮件。
为安全起见,邮箱口令没有写在配置里,Emacs 会自动使用 ~/.authinfo.gpg 文件里的用户登录信息,文件 ~/.authinfo.gpg 是在 init.el 里配置的。
;; 使用 use-package 配置发邮件
(use-package message
:ensure nil ; 内置包
:config
;; 设置发件人信息
(setq user-full-name "XXX"
user-mail-address "subdudu@example1.com")
)
(use-package smtpmail
:ensure nil
:custom
(smtpmail-stream-type 'starttls)
(smtpmail-smtp-server "smtp.qq.com")
(smtpmail-smtp-service 587)
(message-send-mail-function 'smtpmail-send-it))
或者,我使用了 mu4e:
(use-package mu4e
:ensure nil ; 因为 mu4e 通常随 mu 系统级安装,不从 ELPA 安装
:defer t
:commands (mu4e-update-mail-and-index)
:custom
;; 本地 Maildir 路径
(mu4e-maildir (expand-file-name "MailDir" (or (getenv "XDG_DATA_HOME")
"~/.local/share/")))
;; 设置为选择第一个context,如果设置为其它值,就会被询问
(mu4e-context-policy 'pick-first)
;; 设置发送邮件 (SMTP)
;; (smtpmail-stream-type 'ssl)
;; (smtpmail-smtp-server "smtp.qq.com")
;; (smtpmail-smtp-service 465)
;; (user-mail-address "subdudu@example1.com") ; 你的 mail 地址
;; (user-full-name "XXX") ; 你的全名
;; 设置接收邮件 - 使用 mbsync
(mu4e-get-mail-command "mbsync -a") ; 执行这个命令来拉取邮件
(mu4e-attachment-dir "~/Downloads") ; 附件保存目录
;; 其它设置
(mu4e-view-show-images t) ; 在邮件中显示图片
(mu4e-use-fancy-chars t) ; 使用漂亮的图标
:init
;; 每60分钟同步一次,没有设置mu4e-update-interval来同步邮件,它只在mu4e运行中才能起作用
(run-with-timer 60 (* 30 60) '(lambda () (mu4e-update-mail-and-index t)))
:config
(setq mu4e-contexts ;多邮件账户发件环境配置
`(,(make-mu4e-context
:name "example1"
:enter-func (lambda () (mu4e-message "Entering example1 context"))
:leave-func (lambda () (mu4e-message "Leaving example1 context"))
:match-func (lambda (msg)
(when msg
(string-prefix-p "/qq" (mu4e-message-field msg :maildir))))
:vars '((user-mail-address . "subdudu@example1.com")
(user-full-name . "XXX")
(message-user-organization . "Private")
(smtpmail-stream-type . ssl)
(smtpmail-smtp-server . "smtp.qq.com")
(smtpmail-smtp-service . 465)
(mu4e-sent-folder . "/qq/Sent Messages")
(mu4e-drafts-folder . "/qq/Drafts")
(mu4e-trash-folder . "/qq/Deleted Messages")
(mu4e-refile-folder . "/qq/其他文件夹/归档")
))
,(make-mu4e-context
:name "work"
:enter-func (lambda () (mu4e-message "Entering work context"))
:leave-func (lambda () (mu4e-message "Leaving work context"))
:match-func (lambda (msg)
(when msg
(string-prefix-p "/work" (mu4e-message-field msg :maildir))))
;; (lambda (msg)
;; (when msg
;; (mu4e-message-contact-field-matches msg :to "subdudu@example3.com")))
:vars '((user-mail-address . "subdudu@example3.com")
(user-full-name . "XXX")
(message-user-organization . "XXXXXX")
(smtpmail-stream-type . ssl)
(smtpmail-smtp-server . "smtp.exmail.qq.com")
(smtpmail-smtp-service . 465)
(mu4e-sent-folder . "/work/Sent Messages")
(mu4e-drafts-folder . "/work/Drafts")
(mu4e-trash-folder . "/work/Deleted Messages")
(mu4e-refile-folder . "/work/其他文件夹/归档")
))
,(make-mu4e-context
:name "aliyun"
:enter-func (lambda () (mu4e-message "Entering aliyun context"))
:leave-func (lambda () (mu4e-message "Leaving aliyun context"))
:match-func (lambda (msg)
(when msg
(string-prefix-p "/ali" (mu4e-message-field msg :maildir))))
:vars '((user-mail-address . "subdudu@example2.com")
(user-full-name . "XXX")
(message-user-organization . "Private")
(smtpmail-stream-type . ssl)
(smtpmail-smtp-server . "smtp.example2.com")
(smtpmail-smtp-service . 465)
(mu4e-sent-folder . "/ali/已发送")
(mu4e-drafts-folder . "/ali/草稿")
(mu4e-trash-folder . "/ali/已删除邮件")
(mu4e-refile-folder . "/ali/ReservedLetters")
))))
)
经过上述配置,就可以使用 mail 或者 mu4e 来发邮件了。配置里把收取邮件的配置也写进去了,同步邮件使用的是 mbsync 。
2 邮件收取
deepseek 推荐的工具包括 3 类:
- mu4e:基于外部工具 maildir-utils(通常被称为 mu,maildir 是一种邮件本地存储格式)和 isync(邮件同步,支持 maildir 和 imap,提供命令行工具 mbsync),优点是离线工作,速度快,搜索功能强大,与 Org-mode 等 Emacs 生态完美结合。
- GNUS:是 Emacs 自带的超级强大的新闻/邮件阅读器,可以直接通过 IMAP/SMTP 与服务器交互,无需本地工具。不仅支持邮件,还支持新闻组 (NNTP),但学习曲线比 mu4e 更陡峭,在处理大量邮件时速度可能较慢。
- notmuch:是一个基于本地索引的邮件客户端,与 mu4e 类似,但“只索引,不存储”,搜索能力是其核心。
2.1 mu4e(debian 环境)
我选择了 mu4e 方案,其它方案未尝试,首先安装 mu4e 和 isync 这两个外部工具:
sudo apt install mu4e isync
然后配置 mbsync,mbsync 配置成功了,其它就简单了。mbsnync 配置文件较为复杂,分为若干块,块与块之间用空行隔开。这些块分为 Account, Store 和 channel 3 类,Account 类用于配置邮箱服务器凭证,MaildirStore 指定本地存储位置,IMAPStore 指定邮箱账户,channel 则负责将远程与本地连接起来,并指定同步哪些文件夹,如何同步。该配置文件位于 $XDG_CONFIG_HOME/isyncrc ,如果没有就创建一个:
IMAPAccount example1
Host imap.qq.com
User subdudu@example1.com
TLSType IMAPS
AuthMechs LOGIN
PassCmd "gpg --quiet --decrypt ~/.authinfo.gpg 2>/dev/null | awk '/smtp\.qq\.com/ {print $8}'"
IMAPStore example1-remote
Account example1
MaildirStore example1-local
Path ~/.local/share/MailDir/qq/
INBox ~/.local/share/MailDir/qq/Inbox/
SubFolders Verbatim
Channel example1-inbox
Far :example1-remote:
Near :example1-local:
Sync All
Patterns *
Create Both
Expunge None
SyncState *
IMAPAccount aliyun
Host imap.example2.com
User subdudu@example2.com
TLSType IMAPS
AuthMechs LOGIN
PassCmd "gpg --quiet --decrypt ~/.authinfo.gpg 2>/dev/null | awk '/smtp\.aliyun\.com/ {print $8}'"
IMAPStore aliyun-remote
Account aliyun
MaildirStore aliyun-local
Path ~/.local/share/MailDir/ali/
INBox ~/.local/share/MailDir/ali/Inbox/
Trash ~/.local/share/MailDir/ali/已删除邮件/
SubFolders Verbatim
Channel aliyun-inbox
Far :aliyun-remote:
Near :aliyun-local:
Sync All
Patterns *
Create Both
Expunge None
CopyArrivalDate yes
SyncState *
IMAPAccount work
Host imap.exmail.qq.com
User subdudu@example3.com
TLSType IMAPS
AuthMechs LOGIN
PassCmd "gpg --quiet --decrypt ~/.authinfo.gpg 2>/dev/null | awk '/smtp\.exmail\.qq\.com/ {print $8}'"
IMAPStore work-remote
Account work
MaildirStore work-local
Path ~/.local/share/MailDir/work/
INBox ~/.local/share/MailDir/work/Inbox/
SubFolders Verbatim
Channel work-inbox
Far :work-remote:
Near :work-local:
Sync All
Patterns *
Create Both
Expunge None
SyncState *
配置好之后,在终端执行以下命令,即可将邮件同步到本地:
mkdir -p ${XDG_DATA_HOME:-$HOME/.local/share}/MailDir/qq
mkdir -p ${XDG_DATA_HOME:-$HOME/.local/share}/MailDir/ali
mkdir -p ${XDG_DATA_HOME:-$HOME/.local/share}/MailDir/work
mbsync example1-inbox
mbsync 的配置相当复杂,现在还没完全搞明白。反正先拣最重要的“邮箱服务器凭证”和“本地路径”配置好,其它的几乎套模板,没有改动,就能够成功收取邮件了。如果有个性化的需求,可能需要配置其它选项。
接下来是 mu 初始化,根据 XDG Base Directory 规范,我们最好把目录设置在 $XDGDATAHOME 下:
mu init --maildir=${XDG_DATA_HOME:-$HOME/.local/share}/MailDir --my-address=subdudu@example1.com --my-address=subdudu@example2.com --my-address=subdudu@example3.com
然后在 Emacs 中执行 mu4e 函数,就能调了 mu4e 界面了。根据界面提示操作即可。
mu4e 有几个快捷键, d ( mu4e-view-mark-for-trash 或 mu4e-headers-mark-for-trash )将邮件移动到垃圾箱,并打开 T 标记, r ( mu4e-view-mark-for-refile 或 mu4e-headers-mark-for-refile )将邮件归档,这些操作相当于给邮件打上标签,这个标签在界面上可以看到,在实际应用( mu4e-mark-execute-all 或 mu4e-view-marked-execute )前这些标签都可以按 u 移除。
mu4e 可以批量操作邮件,使用 * 给邮件打上“*”标记,然后可以批量执行某个命令,或者使用 % 进行批量操作。
mu4e 可以快速搜索邮件,搜索可以使用正则表达式,如: subject:/退款/, from:/淘宝.+/, body:/read/, /我/ 等。
2.2 后记
在配置多邮箱时,最新的方法是使用 mu4e-context 实现邮箱配置的自动切换。在配置的过程尝试过将 mu4e-sent-folder 等目录设置为一个如下的函数,但发邮件的时候就出问题了,因为 to 字段还没有值,而且有些邮件的 to 字段根本就没有值,用的是 bcc 字段,所以这个方法行不通:
(mu4e-sent-folder (lambda (msg)
(cond
((mu4e-message-contact-field-matches msg :to "subdudu@example1.com")
"/qq/Sent Messages")
((mu4e-message-contact-field-matches msg :to "subdudu@example3.com")
"/work/Sent Messages"))))
这个问题也提醒了我,邮件不一定都有 to 字段,bcc 字段是看不到的,所以在 mu4e-context 设置时我把判断 to 字段修改成了判断 maildir 。
后来想设置一个 default 的 context,但在发送邮件的时候就直接进入到这个 context,没有 user-mail-address:
(make-mu4e-context
:name "default"
:enter-func (lambda () (mu4e-message "Entering default context"))
:leave-func (lambda () (mu4e-message "Leaving default context"))
:match-func (lambda (msg) t)
:vars '((mu4e-sent-folder . "/sent")
(mu4e-drafts-folder . "/drafts")
(mu4e-trash-folder . "/trash")
(mu4e-refile-folder . "/archive")))
3 通讯录
(use-package bbdb
:ensure t
:bind ((:map mu4e-view-mode-map
("C-c b" . bbdb-mua-edit-sender))
(:map mu4e-compose-mode-map
("M-TAB" . bbdb-complete-name)))
:custom
(bbdb-mua-auto-update-p 'query)
(bbdb-file "~/.emacs.d/bbdb")
:config
(bbdb-initialize 'mu4e 'message)
)
4 gnus
Gnus 是 Emacs 的官方包,其主要目标就是阅读新闻,它会把 usenet, email, rss, atom 甚至搜索结果这些看起来相似度极低的服务都作为新闻源。首先设置 Gnus 的工作目录:
(use-package gnus
:ensure nil
:defer t
:custom
(gnus-directory
(expand-file-name "News" (or (getenv "XDG_DATA_HOME")
"~/.local/share/")))
(gnus-select-method '(nntp "news.gwene.org")) ;; RSS 聚合的新闻组
)
5 读取 RSS 新闻
阅读 RSS 使用的是 elfeed,它的配置相比邮件就简单多了。
(use-package elfeed
:ensure t
:defer t
:bind ((:map elfeed-show-mode-map
("RET" . elfeed-show-visit))
(:map elfeed-search-mode-map
("C-RET" . elfeed-search-browse-url)))
:custom
(elfeed-feeds
'("https://feeds.maketecheasier.com/MakeTechEasier"
"https://solar.lowtechmagazine.com/feeds/all-en.atom.xml"
"https://old.reddit.com/r/f1technical.rss"
"https://www.debian.org/News/news"
"https://paper.seebug.org/rss/"
"https://openlanguage.com/ez/podcast/learn-english/pj"
"http://www.stats.gov.cn/sj/zxfb/rss.xml"
"http://www.stats.gov.cn/sj/sjjd/rss.xml"
"https://www.chinanews.com.cn/rss/importnews.xml"
"https://rss.aishort.top/?type=wasi" ;瓦斯阅读
"https://planet.emacslife.com/atom.xml"
))
(line-spacing 0.3) ;增加行距
(elfeed-show-truncate-long-urls nil)
:init
(run-at-time 70 (* 60 60) 'elfeed-update) ;每小时刷新一次
)
阅读前一定要 elfeed-update ( G )刷新,它不会自动刷新。阅读的时候只能看到摘要,看全文要用到浏览器,使用 elfeed-show-visit 可以不用鼠标就能在浏览器打开链接。
更多推荐


所有评论(0)