DraftMachine — Gmail Mail Merge via CLI
DraftMachine creates Gmail drafts from a CSV list + Markdown template. Drafts land in the
user's Gmail Drafts folder for review before sending — nothing gets sent automatically.
Step 1 — Check installation
CODEBLOCK0
If the command is not found, install it:
CODEBLOCK1
Step 2 — Check Gmail credentials
Two files must exist:
| File | Purpose |
|---|
| INLINECODE0 | OAuth app credential downloaded from Google Cloud Console |
| INLINECODE1 |
Cached OAuth token (created automatically by
draftmachine setup) |
Check for them:
CODEBLOCK2
If client_secret.json is missing — walk the user through GCP setup
Tell the user they need to do a one-time setup to connect DraftMachine to their Gmail:
- 1. Go to Google Cloud Console and create a project (or pick an existing one).
- Navigate to APIs & Services → Enable APIs & Services and enable the Gmail API.
- Go to APIs & Services → Credentials → Create Credentials → OAuth client ID.
- Choose Desktop app as the application type.
- Click Download JSON and save the file to
~/.draftmachine/client_secret.json.
- You may need to
mkdir ~/.draftmachine first.
- 6. Run
draftmachine setup — this opens a browser window, asks for Gmail permission (draft-only scope), and saves the token.
If client_secret.json exists but creds.json is missing
Run draftmachine setup to complete the OAuth consent flow.
If both files exist
Credentials are ready — skip to Step 3.
Step 3 — Gather the recipient list and compose the template
CSV file
The CSV needs at minimum an email column (default column name: email). Any other columns become
available as template variables. Ask the user what data they have. If they paste data in the
conversation, write it to a .csv file.
Example:
CODEBLOCK3
Markdown template
The template is a .md file with a YAML frontmatter block for the subject line and a Markdown
body using Jinja2 syntax. Ask the user what the email
should say, then write the template.
CODEBLOCK4
Tips for good templates:
- - Use
{{ variable }} to insert CSV column values. - Wrap optional content in
{% if variable %}...{% endif %} so missing values don't cause awkward blanks. - The subject line supports Jinja2 too.
- Filters like
{{ first_name | title }} and loops are supported.
Step 4 — Preview before creating drafts
Always run --preview first. It renders the first row only to the terminal — no API calls, no
drafts created. This is a fast sanity check for template errors and formatting.
CODEBLOCK5
If the rendered output looks right, proceed. If there are errors (undefined variables, broken
conditionals, etc.), fix the template and re-preview.
Step 5 — Create the drafts
Once the preview looks good:
CODEBLOCK6
If the email address is in a column other than email, use --to-column:
CODEBLOCK7
DraftMachine uses a two-pass strategy: it renders all rows first (aborting early if any row
has template errors), then creates all drafts via the Gmail API. This means it's all-or-nothing
per run — no partial draft batches on template errors.
Step 6 — Report back
After the command completes, tell the user:
- - How many drafts were created.
- That the drafts are in their Gmail Drafts folder, ready to review and send.
- A reminder to check for any rows that were skipped (DraftMachine warns about empty/missing
to addresses in the terminal output).
Error reference
| Error | Fix |
|---|
| INLINECODE20 | INLINECODE21 |
| INLINECODE22 |
Complete GCP + OAuth setup (Step 2) |
|
403 Forbidden | OAuth token lacks correct scope — re-run
draftmachine setup |
|
429 Too Many Requests | Gmail API rate limit hit; DraftMachine retries 3× with backoff. If it persists, wait and re-run |
|
UndefinedError: '...' is undefined | CSV column name in template doesn't match actual column header |
| Partial drafts on 429 | No resume; re-run the full command after a short wait (may create duplicates — delete extra drafts) |
DraftMachine — 通过命令行实现Gmail邮件合并
DraftMachine可根据CSV列表和Markdown模板创建Gmail草稿。草稿会存入用户的Gmail草稿文件夹,供发送前审核——不会自动发送任何内容。
步骤1 — 检查安装
bash
draftmachine --version
如果命令未找到,请安装:
bash
pip install draftmachine
步骤2 — 检查Gmail凭据
需存在以下两个文件:
| 文件 | 用途 |
|---|
| ~/.draftmachine/client_secret.json | 从Google Cloud Console下载的OAuth应用凭据 |
| ~/.draftmachine/creds.json |
缓存的OAuth令牌(由draftmachine setup自动创建) |
检查文件是否存在:
bash
ls ~/.draftmachine/
如果缺少client_secret.json — 引导用户完成GCP设置
告知用户需要进行一次性设置,将DraftMachine连接到其Gmail账户:
- 1. 访问Google Cloud Console并创建一个项目(或选择现有项目)。
- 导航至API和服务 → 启用API和服务,启用Gmail API。
- 前往API和服务 → 凭据 → 创建凭据 → OAuth客户端ID。
- 选择桌面应用作为应用程序类型。
- 点击下载JSON,将文件保存到~/.draftmachine/clientsecret.json。
- 可能需要先执行mkdir ~/.draftmachine。
- 6. 运行draftmachine setup — 这将打开浏览器窗口,请求Gmail权限(仅草稿范围),并保存令牌。
如果client_secret.json存在但缺少creds.json
运行draftmachine setup完成OAuth授权流程。
如果两个文件都存在
凭据已就绪 — 跳至步骤3。
步骤3 — 准备收件人列表并编写模板
CSV文件
CSV文件至少需要一个电子邮件列(默认列名:email)。其他列可作为模板变量使用。询问用户拥有哪些数据。如果用户在对话中粘贴数据,请将其写入.csv文件。
示例:
csv
email,first_name,company
jane@example.com,Jane,Acme Corp
bob@example.com,Bob,
Markdown模板
模板是一个.md文件,包含用于主题行的YAML前置元数据块,以及使用Jinja2语法的Markdown正文。询问用户邮件内容,然后编写模板。
markdown
subject: 给{{ first_name }}的简短留言
你好,{{ first_name }},
{% if company %}
我了解到{{ company }},觉得你可能对此感兴趣。
{% endif %}
[邮件正文内容]
此致,
[发件人姓名]
优秀模板的提示:
- - 使用{{ variable }}插入CSV列值。
- 将可选内容包裹在{% if variable %}...{% endif %}中,避免缺失值导致尴尬的空白。
- 主题行也支持Jinja2语法。
- 支持{{ first_name | title }}等过滤器以及循环。
步骤4 — 创建草稿前预览
始终先运行--preview。它仅在终端中渲染第一行——不调用API,不创建草稿。这是快速检查模板错误和格式的简便方法。
bash
draftmachine send list.csv template.md --preview
如果渲染输出正确,则继续。如果存在错误(未定义变量、条件语句错误等),请修正模板并重新预览。
步骤5 — 创建草稿
预览无误后:
bash
draftmachine send list.csv template.md
如果电子邮件地址位于非email的列中,请使用--to-column:
bash
draftmachine send list.csv template.md --to-column work_email
DraftMachine采用两遍策略:先渲染所有行(如果任何行存在模板错误则提前中止),然后通过Gmail API创建所有草稿。这意味着每次运行要么全部成功,要么全部失败——不会因模板错误而创建部分草稿批次。
步骤6 — 报告结果
命令执行完成后,告知用户:
- - 创建了多少封草稿。
- 草稿位于其Gmail草稿文件夹中,可供审核和发送。
- 提醒检查是否有被跳过的行(DraftMachine会在终端输出中警告空/缺失的收件人地址)。
错误参考
| 错误 | 解决方法 |
|---|
| command not found: draftmachine | pip install draftmachine |
| No such file: client_secret.json |
完成GCP + OAuth设置(步骤2) |
| 403 Forbidden | OAuth令牌缺少正确范围 — 重新运行draftmachine setup |
| 429 Too Many Requests | 达到Gmail API速率限制;DraftMachine会以退避策略重试3次。如果持续出现,请等待后重新运行 |
| UndefinedError: ... is undefined | 模板中的CSV列名与实际列标题不匹配 |
| 429错误导致部分草稿 | 无恢复机制;短暂等待后重新运行完整命令(可能产生重复草稿 — 请删除多余草稿) |