himalaya `message.send.save-copy` 配置详解:让已发送邮件自动归档到指定邮箱

发布时间:2026/10/4 1:42:50
himalaya `message.send.save-copy` 配置详解:让已发送邮件自动归档到指定邮箱 CLI【免费下载链接】himalayaCLI to manage emails项目地址https://gitcode.com/gh_mirrors/hi/himalaya点击查看免费下载导读本文围绕 himalaya 的message.send.save-copy配置项展开讲解如何在发送命令message send以及带--send的message compose、message reply、message forward未显式传入--save时将已发送邮件的副本自动归档到指定邮箱并深入剖析其底层实现Account::resolve_save的解析优先级、handler::apply的“先发送后保存”顺序帮助你按账号配置发送副本策略避免 SMTP 发信后无处留存的问题。背景为什么需要“发送即保存副本”himalaya v2 在重构时移除了 v1 中的message.send.save-copy配置。结果就是通过 SMTP 发送的邮件除非每次调用命令都显式传--save否则发送成功后不会有任何副本留存。而不同的邮件服务商行为并不一致Gmail 和 Microsoft Graph服务端会自动把已发送邮件归档进 Sent 文件夹无需客户端保存副本SMTP如自建服务器、Fastmail/Posteo 的 SMTP 通道服务端不会保存副本发送后本地无痕。按账号来配置这个需求尤为关键同一个 himalaya 实例可能同时管理 Gmail 与 SMTP 账号前端himalaya-emacs、himalaya-vim、himalaya-tui无法预知每个账号走的是哪种后端。与其在每个前端各自添加保存选项不如在核心配置层为每个账号指定“发件副本去哪”。另外旧行为还有一个隐患--save是在发送之前追加副本的。如果发送失败就会留下一个“从未真正发出”的邮件副本。新实现将保存顺序调整为“先发送、后保存”彻底规避了这个问题。配置项message.send.save-copy取值类型message.send.save-copy支持两种取值形式由 src/config.rs 中的SaveCopyConfig枚举通过#[serde(untagged)]反序列化解析写法含义示例布尔值true表示“sent”角色邮箱即 v1 的语义message.send.save-copy true布尔值false不保存副本v1 语义message.send.save-copy false字符串邮箱名、别名alias或角色rolemessage.send.save-copy Sent Items、message.send.save-copy sent字符串解析与--save完全一致可以是邮箱的实际名称如Sent Items、配置在mailbox.alias中的别名或者角色名如sent、inbox、archive。全局与账号级覆盖该配置可以写在全局[message.send]区域也可以写在账号的[accounts.name]中账号级覆盖全局。这一“全局 每账号覆盖”的合并逻辑由 Account::merge 实现save_copy: other.save_copy.or(self.save_copy)即账号配置存在时优先采用账号值否则回退到全局值。参考 config.sample.toml 中的官方注释示例# 全局配置所有账号默认将已发送副本存到 sent 邮箱 [message.send] save-copy sent [accounts.example] email aliceexample.org # 账号级覆盖该账号发送副本存入 Sent Items #message.send.save-copy Sent Items来自 config.sample.toml 与 config.sample.toml 的说明还特别提醒Gmail 和 Microsoft Graph 账号应当保持该项不设置因为这两个后端自己会归档已发送邮件重复保存反而多此一举。v1 兼容message表开放未知键himalaya v1 的[message]表包含read、write、delete、send.backend等大量键。为保证 v1 配置在 v2 中继续加载MessageConfig 特意不再使用deny_unknown_fields#[serde(rename_all kebab-case)] pub struct MessageConfig { #[serde(default)] pub send: MessageSendConfig, }也就是说message表接受未知键v1 的[message]表其余内容照常解析不会因出现新版本不认识或已移除的键而整体报错。这一点由单元测试 save_copy_reads_a_v1_boolean_beside_v1_keys 直接验证同时携带message.send.save-copy true、message.send.backend smtp、message.read.format plain的 v1 风格配置可以完整反序列化且save-copy true被解析为sent角色。命令行--save与--no-save新增--no-save为了让用户在一次调用中跳过配置好的副本四个发送命令都新增了--no-save标志message sendmessage compose配合--send发送时message reply配合--send发送时message forward配合--send发送时以 send.rs 中的MessageSendCommand为例/// Append a copy of the sent message to this mailbox name, alias or /// role, overriding message.send.save-copy. #[arg(long, value_name MAILBOX)] pub save: OptionString, /// Skip the copy message.send.save-copy configures. #[arg(long, conflicts_with save)] pub no_save: bool,注意#[arg(long, conflicts_with save)]--no-save与--save互相冲突同时传入会直接报错退出避免语义歧义。compose、reply、forward三个命令的 CLI 定义与此完全同构见 compose.rs、reply.rs、forward.rs。优先级实际生效的保存目标由 Account::resolve_save 统一裁决优先级从高到低--save mailbox显式指定永远优先覆盖一切配置--no-save本次发送跳过副本message.send.save-copy配置作为兜底两者皆无且未发送不保存。核心逻辑pub fn resolve_savea( a self, over: Optiona str, no_save: bool, send: bool, ) - Optiona str { if over.is_some() || no_save || !send { return over; } match self.save_copy.as_ref()? { SaveCopyConfig::Enabled(true) Some(MailboxRole::Sent.as_str()), SaveCopyConfig::Enabled(false) None, SaveCopyConfig::Mailbox(mailbox) Some(mailbox), } }这段代码还揭示了一个重要语义配置的副本只在“发送”时生效。即使用户配置了save-copy如果只是执行不带--send的message compose仅撰写不发送resolve_save(..., send false)也会返回None不会把草稿写进副本邮箱。保存草稿仍需显式--save。这一点由测试 resolve_save_falls_back_on_the_copy_only_when_sending 锁定assert_eq!(account.resolve_save(None, false, true), Some(Sent Items)); // 发送时回退到配置 assert_eq!(account.resolve_save(None, false, false), None); // 不发送时忽略配置 assert_eq!(account.resolve_save(Some(Archive), false, true), Some(Archive)); // --save 覆盖 assert_eq!(account.resolve_save(None, true, true), None); // --no-save 跳过底层实现先发送、后保存handler::apply的执行顺序无论是--save与发送命令组合还是message add --send保存与发送的统一执行入口都是 handler::apply。其顺序保证为先发送client.send_message(sent, raw.clone())?发送失败则整个命令以错误退出不留下任何副本后保存发送成功后再client.add_message(mailbox, flags, raw)追加副本保存失败发送已成功、保存失败时错误上下文明确告知“邮件已发送但副本保存失败”。对应关键代码// NOTE: a deferred send is filed under the saved copys mailbox, else // under the one the account names as sent. let queued match send { true { let sent mailbox.or_else(|| account.mailbox_alias.get(sent).map(String::as_str)); client.send_message(sent, raw.clone())? } false None, }; let saved_id match mailbox { Some(mailbox) if send Some( client .add_message(mailbox, flags, raw) .with_context(|| format!(Message sent, but saving a copy to {mailbox} failed))?, ), ... };当发送失败时send_message返回错误后续的add_message根本不会执行——这正是“失败发送不产生副本”的保证来源。相关细节副本附加\Seen标志handler::route调用apply时传入[Flag::from_iana(IanaFlag::Seen)]即保存的副本会被标记为已读见 handler.rs。副本邮箱经别名解析apply中的let mailbox save.map(|name| account.resolve_mailbox(name))会先经过mailbox.alias别名解析再落到后端见 handler.rs。输出提示发送并保存成功输出Message successfully saved and sent仅保存输出Message successfully saved仅发送输出相应提示见 handler.rs。发送回退邮箱发送时若无副本邮箱send_message会回退到mailbox.alias中名为sent的别名保证 pimdir 等延迟发送queued send场景下发送记录能落到正确邮箱。各命令用法速查以下命令均适用于配置了save-copy的账号# 发送原始消息文件并自动把副本存到 save-copy 配置的邮箱 himalaya message send --file ./message.eml # 发送并用 --save 临时覆盖配置存到 Archive himalaya message send --file ./message.eml --save Archive # 本次发送不保存任何副本 himalaya message send --file ./message.eml --no-save # 撰写并发送自动保存副本 himalaya message compose --send # 回复并发送自动保存副本 himalaya message reply --send 42 # 转发并发送自动保存副本 himalaya message forward --send 42要点总结--no-save与--save冲突不能同时使用--save优先级高于配置文件副本只在“发送”动作发生时自动保存纯撰写不会触发。已知限制与后续规划依据 proposal.md 的说明本变更落地时明确了两个未覆盖的边界向导wizard不会写入该配置himalaya wizard生成的配置不含save-copy因为 IMAP 后端要等imap-special-use-aliasesLISTRETURN (SPECIAL-USE)能力落地后才能可靠解析sent角色在此之前向导生成一个save-copy sent会在每次 IMAP 发送副本时失败。IMAP 的sent角色依赖在 SPECIAL-USE 支持到位前IMAP 账号需手动配置mailbox.alias.sent或用真实邮箱名作为save-copy值副本保存才能指向正确位置。验证记录与源码导读该变更已随 himalaya 落地cairn 状态status: landed2026-10-01并完成了完整的端到端验证见 cairn/log/2026-10-01-message-send-save-copy.md使用 Maildir 账号加脚本化 SMTP 服务器覆盖了“配置副本生效”“--no-save跳过”“--save覆盖配置”“发送被拒不留副本”“副本保存失败时报告已发送”“compose --send组合”六类场景。如需深入源码建议按以下路径阅读配置解析src/config.rsMessageConfig/MessageSendConfig/SaveCopyConfig账号合并与解析src/account/context.rsAccount::resolve_save及同文件测试段 src/account/context.rs命令定义src/shared/message/send.rs、compose.rs、reply.rs、forward.rs发送/保存统一入口src/shared/message/handler.rs样例配置config.sample.toml通过message.send.save-copyhimalaya 把“发件副本去哪”的决定权收敛到了每个账号的配置文件里配合--save/--no-save的临时覆盖以及“先发送后保存”的顺序保证为多后端、多账号场景下的发件留档提供了统一且可靠的方案。赞分享CLI【免费下载链接】himalayaCLI to manage emails项目地址https://gitcode.com/gh_mirrors/hi/himalaya点击查看免费下载相关推荐himalaya v2 配置指南用 message.send.save-copy 让每次发送都自动归档副本himalaya v2 配置指南用 message.send.save copy 让每次发送都自动归档副本 message.send.save copy 是CLIhimalaya 邮件发送副本保存message.send.save-copy 配置解析与先发送后保存语义himalaya 邮件发送副本保存 message.send.save copy 配置解析与先发送后保存语义 本文围绕 himalaya v2 新增的 mCLIInstatic 多语言支持一个字段到整站多语言的最快路径Instatic 多语言支持一个字段到整站多语言的最快路径 给站点加个日语版你大概打算把整站页面手动复制一遍。复制了二十页卡住了导航还指着英文版。InstCLI上一篇还在为戴森球计划工厂布局抓狂这份开源蓝图仓库让你3分钟变建造大师下一篇如何快速优化Pi-Hole体验whitelist工具完整使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询