邮箱发票自动整理
从指定邮箱搜索发票邮件 → 下载附件 → 合并发票+行程单PDF → 生成费用清单Excel → 放到桌面。
依赖技能
- imap-smtp-email — 邮件搜索与附件下载
- MinerU Document Extractor — PDF文本提取(获取发票关键信息)
- invoice-pdf-merge — 发票+行程单PDF智能合并
- minimax-xlsx — 费用清单Excel生成
运行前检查(每次必做)
在开始工作流程之前,先执行以下检查:
cat /Users/lulingyan/.workbuddy/skills/imap-smtp-email/.env 2>/dev/null
判断逻辑:
.env存在且包含IMAP_PASS=非空值 → 配置已完成,跳到工作流程- 否则 → 暂停,执行下方首次授权引导
首次使用:邮箱授权引导
按以下步骤引导新用户完成配置(用自然语言与用户交互,不要直接写文件):
第1步:询问邮箱地址
"请问你的发票邮箱地址是?(例如 517582987@qq.com)"
第2步:根据邮箱后缀判断服务商
| 后缀 | IMAP Host | 端口 | SMTP Host | 端口 | 密码类型 | |------|-----------|------|-----------|------|---------| | @163.com / @vip.163.com | imap.163.com | 993 | smtp.163.com | 465 | 授权码 | | @126.com / @vip.126.com | imap.126.com | 993 | smtp.126.com | 465 | 授权码 | | @qq.com | imap.qq.com | 993 | smtp.qq.com | 587 | 授权码 | | @gmail.com | imap.gmail.com | 993 | smtp.gmail.com | 587 | App Password | | @outlook.com | outlook.office365.com | 993 | smtp.office365.com | 587 | 正常密码 |
第3步:提醒获取授权码(关键!)
根据服务商提示用户:
- 163/126/QQ:登录网页邮箱 → 设置 → POP3/SMTP/IMAP → 开启 IMAP → 生成授权码(不是登录密码!)
- Gmail:Google 账户 → 安全性 → 两步验证 → 应用专用密码
- 详细步骤参考
imap-smtp-emailSKILL.md 的 Configuration 章节
第4步:帮用户生成 .env 文件
收集到邮箱地址和授权码后,用 Write 工具写入:
路径:/Users/lulingyan/.workbuddy/skills/imap-smtp-email/.env
(参考 imap-smtp-email SKILL.md 中 Configuration 章节的模板)
第5步:测试连接
cd /Users/lulingyan/.workbuddy/skills/imap-smtp-email
node scripts/imap.js check --limit 1
- 成功 → 显示最近一封邮件标题,告知用户"邮箱配置成功,开始处理发票"
- 失败 → 根据报错引导排查(见下方常见问题)
常见错误:
| 报错 | 原因 | 解决方案 |
|------|------|---------|
| Authentication failed | 授权码错误,或未开启 IMAP | 重新生成授权码,确认 IMAP 已开启 |
| Connection timeout | 主机名/端口错误 | 核对上表,确认网络可访问对应端口 |
| unrecognized arguments | 脚本参数名错误 | 检查 merge_invoices.py 参数名是否为 gen_excel(非 generate_excel) |
工作流程
第一步:搜索邮件
cd /Users/lulingyan/.workbuddy/skills/imap-smtp-email
node scripts/imap.js search --recent 24h --limit 50
从输出中找到含发票附件的邮件 UID、发件人、主题。
第二步:下载附件
# 单封邮件全部附件
node scripts/imap.js download <UID> --dir <工作目录>
# 指定附件名
node scripts/imap.js download <UID> --dir <工作目录> --file "文件名.pdf"
工作目录:/Users/lulingyan/WorkBuddy/2026-06-13-20-55-46/invoices_YYYYMMDD/(按当天日期命名)
第三步:解压 zip(12306 邮件)
cd <工作目录>
unzip -o *.zip 2>/dev/null
第四步:用 MinerU 提取所有发票和行程单信息(必须保存并读取结果)
对每一张发票 PDF 和行程单 PDF,分别用 MinerU 提取文本,保存结果到独立文件,必须读取所有结果文件后再进入下一步:
# 对每封邮件的发票PDF和行程单PDF各跑一次
mineru-open-api flash-extract "发票文件.pdf" -o /tmp/inv_<序号>.md
mineru-open-api flash-extract "行程单文件.pdf" -o /tmp/trip_<序号>.md
关键:提取完成后,必须逐一读取 /tmp/inv_.md 和 /tmp/trip_.md,从中解析出:
- 发票号码、开票日期、销售方名称、不含税金额、税额、价税合计
- 行程单:上车时间、出发地、目的地、车型、金额
这些解析出的数据必须全部用于第六步生成 Excel,不得省略或用占位符代替。
⚠️ 常见错误:只跑 MinerU 但不读结果 → Excel 里出现"(行程单见合并PDF)"占位文字,用户需要额外打开 PDF 查看。
第五步:合并发票PDF
/usr/bin/python3 /Users/lulingyan/.workbuddy/skills/invoice-pdf-merge/scripts/merge_invoices.py <工作目录> <工作目录>/merged
脚本会自动:
- 按文件名【】前缀配对发票+行程单
- 上下布局合并输出到
merged/目录 - 12306 纯数字文件名不会自动配对,需手动合并(见下方)
手动合并两张火车票(12306): 用 PyMuPDF 将两张 PDF 上下拼合到一张 A4:
import fitz
a = fitz.open("票1.pdf")
b = fitz.open("票2.pdf")
out = fitz.open()
page = out.new_page(width=595, height=842)
page.show_pdf_page(fitz.Rect(0, 0, 595, 421), a, 0)
page.show_pdf_page(fitz.Rect(0, 421, 595, 842), b, 0)
out.save("merged/12306火车票_往返_合并.pdf")
第六步:生成费用清单 Excel(必须使用 MinerU 提取结果)
用 openpyxl 生成含两个 Sheet 的 Excel,所有数据来源必须是第四步 MinerU 提取的结果文件,不得用占位符:
读取以下文件获取数据:
/tmp/inv_*.md→ 发票号码、开票日期、销售方、不含税金额、税额、价税合计/tmp/trip_*.md→ 上车时间、出发地、目的地、车型、金额
Sheet1「费用清单」列: 序号、发票号码、开票日期、销售方、项目名称(含行程描述)、金额(不含税)、税额、价税合计
Sheet2「行程单汇总」列: 序号、日期、时间、交通工具、车次/服务商、出发地、目的地、座位/车型、金额
参考格式:
- 标题:
2026年X月X日-X日 差旅报销费用清单(合并单元格居中) - 表头:蓝色底白字(4472C4)
- 合计行:红色加粗,金额右对齐
第七步:放到桌面(文件夹形式)
# 在桌面创建当天日期文件夹
mkdir -p ~/Desktop/发票整理_YYYYMMDD
# 将 merged/ 所有文件移入桌面文件夹
cp <工作目录>/merged/* ~/Desktop/发票整理_YYYYMMDD/
命名规则:文件夹名 =
发票整理_YYYYMMDD,例如发票整理_20260613目的:避免所有文件直接散落在桌面,保持桌面整洁。
第八步:清理工作区(可选)
rm -f <工作目录>/*.ofd <工作目录>/*.zip <工作目录>/*.xml
如果用户说"可以删了"或"清理一下",则删除整个工作目录。
注意事项
- 12306 发票:文件名是纯数字,无【】前缀,
merge_invoices.py不会自动配对,需手动合并两张往返票 - 高德打车:发件人通常是"高德打车"或聚合出行服务商(风韵、妥妥E行、快来车、雷利出行),附件含发票PDF + 行程单PDF 各一份
- 邮箱配置:读取
~/.workbuddy/skills/imap-smtp-email/.env获取邮箱地址和密码 - Excel 预览问题:
open_result_view对 .xlsx 支持不好,需用 HTML 渲染预览或让用户直接用 Excel 打开
微信扫一扫