email-sender — 批量个性化发信
按 Excel 发件清单,为每一行收件人渲染带 {列名} 变量的邮件正文并群发。支持三种正文来源(见下文三种交互模式),发送引擎支持 SMTP(默认)与 本地 Outlook(仅 Windows)。
技能内置资产
assets/email_template.html— 通用正式排版 HTML 邮件模板,内置{邮件标题}/{姓名}/{部门}/{月份}/{绩效等级}占位符,作为模式 2 的默认选择,开箱即用;也可被用户自备模板替换。
何时使用
用户表达以下意图时触发:批量发邮件、按 Excel 发信、邮件群发、给名单里的人发信、用模板给清单发信、send batch emails 等。典型场景:把按某列拆分 Excel 得到的 拆分结果/ 子文件,逐一发给对应的人(配合 excel-splitter 技能)。
前置条件(依赖与凭证)
1. Python 依赖
脚本需要 pandas 与 openpyxl。优先使用 WorkBuddy 自带的 managed Python,并在专用 venv 中安装一次:
- managed Python 路径含当前用户主目录:C:\Users\<USER>\.workbuddy\binaries\python\versions\<version>\python.exe(找不到则回退 python3 3.10+)。
- 建 venv(可选但推荐):<managed_python> -m venv <home>\.workbuddy\binaries\python\envs\default,随后用 <venv>\Scripts\python.exe(Windows)或 <venv>/bin/python(macOS/Linux)运行脚本。
- 安装依赖:<python> -m pip install -r <skill_dir>/requirements.txt(已固定 pandas==3.0.5、openpyxl==3.1.5)。
关键:脚本本身不包含任何 SMTP 账号/密码,也不应写入任何密钥。 凭证通过以下顺序解析(优先级从高到低):命令行 --smtp-* 参数 > 同目录/当前目录的 mail_config.json > 环境变量 SMTP_HOST/SMTP_PORT/SMTP_USER/SMTP_PASS > 占位默认值。为安全起见,优先用环境变量方式提供凭证,不要使用 --smtp-pass 命令行参数(会进入进程列表/历史)。Outlook 模式无需 SMTP 凭证,直接使用本机 Outlook。
2. 凭证获取流程(务必遵守)
- 向用户询问发送方式:SMTP 还是 Outlook(Outlook 仅 Windows 桌面端可用)。
- 若 SMTP:请用户提供
SMTP_HOST、SMTP_PORT、SMTP_USER、以及授权码/密码(注意:QQ/163 等邮箱的密码通常是"授权码"而非登录密码)。在运行脚本前通过环境变量注入,切勿写进文件或回显到聊天。 - 若 Outlook:确认本机已安装并登录 Outlook 桌面客户端即可,无需凭证。
三种交互模式(确定用哪种,并按对应流程处理)
模式 1:聊天框自然语言正文(最常用)
用户直接在对话里写下正文,变量用 {列名} 包裹(列名必须与 Excel 中的列一致)。
- Agent 操作:把用户这段话保存为临时模板文件(放当前工作目录,如 body_template.txt;若含 HTML 标签则用 .html),然后把它作为 -t 参数传给脚本。脚本会自动把纯文本包装成带排版的 HTML。
- 示例指令:"帮我给 发件清单.xlsx 里的所有人发邮件,附件对应 拆分结果/ 下的文件。正文:亲爱的{姓名}({部门}):您好!附件是您{月份}的绩效汇总表,您的绩效评级为{绩效等级},请查收。"
模式 2:使用 HTML 模板文件(内置默认模板,开箱即用)
本技能内置一份通用正式排版模板 assets/email_template.html(含 {邮件标题} {姓名} {部门} {月份} {绩效等级} 变量),无需自备即可直接选用;也可以使用用户自己的 HTML 模板。
- Agent 操作:把模板 .html 文件路径作为 -t 参数。
- 用内置模板:路径为 <skill_dir>/assets/email_template.html,<skill_dir> = 本技能安装目录(用户级 ~/.workbuddy/skills/email-sender,项目级 <repo>/.workbuddy/skills/email-sender)。运行时请用该绝对路径拼接后传入。
- 用用户模板:直接用用户提供的 .html 路径。
- 脚本会原样将其作为 HTML 正文(wrap_plain_text_to_html 检测到 <html/<body/<div 即不二次包装)。
- 模板里可用 {邮件标题} 引用本封邮件的主题(脚本会自动注入)。
模式 3:Agent 代写正文并自动生成模板
用户连正文都不想写,只给主题与需要的变量。
- Agent 操作:根据用户的提示词,生成一段带 {变量} 的 HTML 正文(变量名取自 Excel 列),保存为临时 .html 文件,再作为 -t 传入。生成时注意用清晰的中文排版与 {列名} 占位。
> 三种模式最终都归结为:准备一个"正文模板"(临时文件或用户提供的 HTML),再用脚本批量套用每行数据发送。
Agent 标准工作流程
- 确认发件清单 Excel:定位用户说的清单文件(如
发件清单.xlsx/发信清单.xlsx)。读取并核对列名,特别是必须有收件人列。 - 确认正文来源:按上面三种模式之一,产出正文模板文件(路径记为
TEMPLATE)。 - 确认附件映射(如涉及):若邮件需附带
拆分结果/下的文件,确保 Excel 中存在附件列,其每行指向对应文件路径(多个用;或,分隔)。如清单里还没有该列,用pandas帮用户生成(例如按某列值拼接拆分结果/{值}.xlsx)。 - (可选)主题列:若需个性化主题,在 Excel 加
主题列(支持{变量});否则默认主题为"通知报表"。 - 安全预览(必须):先用
--dry-run运行,渲染首封邮件并打印预览(收件人/主题/附件/正文片段)与总数。把预览结果呈现给用户核对。 - 显式确认后再真发:确认无误后,移除
--dry-run正式发送。发送真实邮件属于对外动作,未经用户明确同意不得执行。 - 回报结果:运行结束后向用户汇报成功/失败数量,并说明报告 Excel 的保存路径(
-o指定)。失败的行需展示原因。
CLI 参考
python <skill_dir>/scripts/email_sender.py \
-f <发件清单.xlsx> \ # 必填:发送配置 Excel
-t <模板文件或正文文本> \ # 必填:HTML 模板路径,或直接的正文文本(脚本会自动判断)
-e smtp|outlook \ # 发送引擎,默认 smtp
--dry-run \ # 模拟模式:只渲染校验,不真实发送(务必先跑一次)
-o <report.xlsx> \ # 可选:导出发送结果报告
--smtp-host <host> --smtp-port <port> --smtp-user <user> --smtp-pass <pass>
# SMTP 凭证(建议改用环境变量,避免明文)
列与变量约定(读取发件清单 Excel)
- 收件人(必填):识别列名
收件人/收件人邮箱/to/email/Email;空值行会被跳过。 - 抄送(可选):
抄送/抄送人/cc/CC。 - 附件(可选):
附件/附件路径/attachment/attachments,多个用;或,分隔。 - 主题(可选):
主题/邮件主题/subject,支持{变量};缺省为"通知报表"。 - 其余任意列:都可作为模板里的
{列名}变量被替换。脚本额外注入{邮件标题}(即本封解析后的主题)。 - 变量替换对"文件模板路径存在"与"直接文本"两种
-t形态都生效。
坑点与注意事项
- 凭证安全:绝不硬编码、不回显 SMTP 密码/授权码;优先环境变量。Outlook 模式免凭证但仅限 Windows 桌面 Outlook。
- 真实发送前必须 dry-run 预览并获确认;误发难以撤回。
- HTML 模板含 CSS 花括号时,脚本的
.format()会失败并自动回退到逐变量replace,不影响{变量}替换,属正常现象。 {列名}必须严格匹配 Excel 列名(含空格/大小写)。变量名拼错会导致该处显示为空或原样保留{xxx}。- 模板里若要显示字面大括号,需注意转义;常规正文模板只用
{列名}即可。 - 依赖只需
pandas/openpyxl;Outlook 模式额外需要pywin32(Windows)。