FengTang(冯唐)
English | 中文
名字由来:“FengTang(冯唐)”取自古人云中传书的典故——鸿雁寄语、 驿马传书,那位穿越云中郡递送家书的信使。借其名,愿每一封邮件都如当年 云中家书:被忠实携带,被温柔送达。
纯 Python 命令行邮件客户端 —— 收、发、读、搜、标记 —— 内置邮件服务器,并提供完整的智能体(function-calling)API。
GPL-3.0-or-later 许可。零运行时依赖:SMTP、IMAP、POP3 客户端,asyncio 实现的 SMTP+POP3 服务器,SQLite 本地存储,MIME 解析,以及 NTLM 所需的 DES/MD4 等全部 SASL 认证,均基于 Python 标准库自行实现。
功能特性
- 全部主流协议:SMTP(发送)、IMAP4(收取/搜索/标志/文件夹)、POP3(收取)——全面支持 SSL 与 STARTTLS。
- 全部主流认证方式:SASL
PLAIN、LOGIN、CRAM-MD5、XOAUTH2/OAUTHBEARER、NTLM(内置纯 Python DES + MD4),以及 POP3 的APOP。auto模式按服务器通告自动选择。 - 服务商预设:gmail、outlook、qq、163、126、yahoo、icloud、zoho、aliyun、sina——一个参数填好全部服务器与端口。
- 内置邮件服务器:没配服务器?
fengtang serve即可启动本地 SMTP + POP3 服务器(asyncio、零系统依赖),支持按邮箱认证、CRAM-MD5/APOP,并带中继防护。 - 本地 SQLite 存储:每封收取/发出的邮件都可离线搜索、标记、移动、删除。
- MIME 处理完善:multipart/alternative、RFC 2047 中文头、附件(列出 + 保存)、HTML→纯文本兜底。
- 智能体 API:每个操作都有对应的 OpenAI function-calling
TOOLSschema +dispatch(),LLM 智能体能像人一样收发、搜索、标记邮件。
安装
要求 Python ≥ 3.10。运行时无任何第三方依赖,全部标准库。
# 推荐:直接装入现有 conda 环境(不需要 venv)
conda activate dev
pip install -e .[dev] # [dev] 只额外装 pytest/ruff/mypy
只使用 CLI/API 的话,pip install -e . 即可。
数据与文件位置
所有运行时数据集中在一个目录:默认 ~/.fengtang/。
| 文件 | 用途 | 权限 |
|---|---|---|
~/.fengtang/config.json |
账号凭据(邮箱、授权码/token、服务器地址、端口) | 0600(保存时强制,仅属主可读写) |
~/.fengtang/fengtang.db |
SQLite 存储:每封收取/发出的邮件——原始 RFC822 报文、解析后的头/正文、标志、附件 | 0600(首次运行后收紧) |
目录解析优先级(从高到低):
config.json内的data_dir字段- 环境变量
FENGTANG_DATA_DIR - 默认
~/.fengtang/
邮件数据库同时保存完整原始报文与解析后的字段(主题、发收件人、日期、标志、text/html 正文),因此可离线搜索,附件可随时重新提取。任何真实凭据或邮件内容都不会出现在代码、测试或文档中。
整体迁移(config + 邮件):
export FENGTANG_DATA_DIR=/Volumes/SecureUSB/fengtang
或在配置文件里设 "data_dir": "/path"。
配置账号(服务商预设)
fengtang config add qq you@qq.com --password <SMTP授权码> --auth login --provider qq
fengtang config test -a qq # 探测 IMAP + POP3 + SMTP 认证
交互式 OAuth 登录(Gmail / Outlook)
Gmail、Outlook 这类 XOAUTH2 账号无需手动找 token,一条命令走浏览器授权:
# 推荐:引导式一键配置(打开控制台教你建 client_id,粘贴后自动继续登录)
fengtang setup-gmail you@gmail.com
# 已有 client_id 时,直接登录
fengtang config login you@gmail.com --provider gmail --client-id <id>
fengtang config login -a 已有账号名 # 对已有账号重新授权
Google 已不允许 Gmail scope 使用公共 client_id,因此需要你自己建一个 (桌面应用类型,无需审核,自己的账号即可用)。
浏览器打开 Google/Microsoft 授权页 → 登录 → 本地回环端口自动接收跳转,
access/refresh token 落盘 ~/.fengtang/config.json(0600)。access token
过期会在下次使用时自动用 refresh token 续期。
收取、列览、阅读、搜索、标记
fengtang fetch -a qq -n 20 # 拉取新邮件进本地库
fengtang list --unread # 按时间倒序
fengtang search "关键词"
fengtang read 42 # 头部 + 正文
fengtang read 42 --save-attachments ./att
fengtang mark 42 --flags seen,flagged
fengtang folders
发送
fengtang send -t bob@example.com -s "你好" -m "正文" \
--attach ./report.pdf:report-2026.pdf
运行内置服务器
# 无认证模式,localhost,存入 ~/.fengtang/fengtang.db
fengtang serve --smtp-port 2525 --pop-port 1110 --domain localhost
# 带认证的邮箱(--user 可重复)
fengtang serve --user alice@localhost:secret1 --user bob@localhost:secret2
随后任何 SMTP/POP3 客户端都可连 127.0.0.1:2525 / 127.0.0.1:1110。本地域投递无需认证;向其他域中继需要认证,否则拒绝(550)。
脚本化 JSON 输出
所有命令都接受 --json,统一返回 {"success", "data", "error", "metadata"}:
fengtang list --json --unread | jq '.data[0].subject'
智能体集成(OpenAI Function Calling)
from fengtang.agent.tools import TOOLS, dispatch
# 1. 把 TOOLS 加进模型的工具列表。
# 2. 模型发起工具调用时路由:
result = dispatch("fengtang_send", {
"to": ["bob@example.com"],
"subject": "智能体发来的问候",
"body": "通过 fengtang 工具调用发送。",
})
print(result) # {"success": True, "data": {...}, "error": None, "metadata": {...}}
可用工具:fengtang_send、fengtang_list、fengtang_read、fengtang_search、fengtang_mark、fengtang_delete、fengtang_move、fengtang_fetch、fengtang_folders、fengtang_account_add、fengtang_account_list、fengtang_account_test。
查看 schema:fengtang api --schema。
Python API
from fengtang import ToolResult, send_mail, list_messages, read_message, search_messages, mark_messages
result = send_mail(to=["bob@example.com"], subject="Hi", body="Hello")
if result.success:
print(result.data["message_id"])
所有 API 函数返回 ToolResult dataclass(success、data、error、metadata、.to_dict()、__bool__ 真值判断)。
配置
账号存于 ~/.fengtang/config.json(0600)。完整布局与迁移方法见上文数据与文件位置。运行期覆盖:
FENGTANG_DATA_DIR— 数据目录fengtang serve --db <path>— 内置服务器的自定义数据库路径
项目结构
fengtang/
├── core/ # config(账号/预设)、errors(ToolResult)、
│ # auth(PLAIN/LOGIN/CRAM-MD5/XOAUTH2/NTLM/APOP)、
│ # _des + _md4(NTLM 所需纯 Python 密码学原语)
├── mail/ # smtp_client、imap_client、pop_client、
│ # parser(MIME 构建/解析/渲染)、store(SQLite)
├── serve/ # 内置 asyncio SMTP + POP3 服务器
├── agent/ # OpenAI function-calling TOOLS + dispatch
└── cli/ # argparse CLI(config/send/fetch/list/read/search/…)
tests/ # pytest 套件,含客户端↔内置服务器端到端回环
开发
conda activate dev # 用现有环境,无需 venv
pip install -e .[dev]
pytest # 59 个测试
ruff check . && ruff format .
mypy fengtang
备注
- QQ/163/126 邮箱使用"授权码"而非登录密码——通过
--password传入。凭据只存于~/.fengtang/config.json(0600),绝不落入代码、测试或文档。 - XOAUTH2 token 可用
fengtang config test -a acct --oauth2-token <token>或Account.oauth2_token提供(Gmail/Outlook OAuth 流程)。 - 内置服务器把投递的邮件存进同一个 SQLite 库,因此
fengtang fetch --protocol pop3可以对它完整回环——测试智能体时不必碰真实邮箱。 - 开发直接使用 conda 环境(如
conda activate dev && pip install -e .[dev])——无需 virtualenv。
许可
GPL-3.0-or-later(见 LICENSE)。
FengTang(冯唐)
English | 中文
About the name: “FengTang (冯唐)” echoes the ancient tale of yun zhong chuan shu (云中传书) — the courier who carried letters through the clouds and frontier posts. May every message you send with FengTang be carried as faithfully as those old family letters.
Pure-Python command-line mail client — send, fetch, read, search, mark — with a built-in mail server and a full agent (function-calling) API.
GPL-3.0-or-later licensed. Zero runtime dependencies: everything (SMTP, IMAP, POP3 clients, an asyncio SMTP+POP3 server, SQLite storage, MIME parsing, SASL auth incl. DES/MD4 for NTLM) is built on the Python standard library.
Features
- All mainstream protocols: SMTP (send), IMAP4 (fetch/search/flags/folders), POP3 (fetch) — SSL and STARTTLS everywhere.
- All mainstream auth methods: SASL
PLAIN,LOGIN,CRAM-MD5,XOAUTH2/OAUTHBEARER,NTLM(with a pure-Python DES + MD4), and POP3APOP.automode tries what the server advertises. - Provider presets: gmail, outlook, qq, 163, 126, yahoo, icloud, zoho, aliyun, sina — one flag fills all host/port settings.
- Built-in mail server: no server configured? Run
fengtang serveto start a local SMTP + POP3 server (asyncio, zero system dependencies) with optional per-mailbox auth, CRAM-MD5/APOP support and relay protection. - Local SQLite store: every fetched/sent message is searchable, flaggable, movable, deletable — offline.
- MIME done right: multipart/alternative, RFC 2047 CJK headers, attachments (list + save), HTML→text fallback.
- Agent API: an OpenAI function-calling
TOOLSschema +dispatch()for every operation, so an LLM agent can read/write/search/mark mail exactly like a human user.
Installation
Requires Python ≥ 3.10. No runtime dependencies — everything is standard library.
# Recommended: install into your existing conda env (no venv needed)
conda activate dev
pip install -e .[dev] # [dev] adds pytest/ruff/mypy only
pip install -e . alone is enough for CLI/API usage.
Data & file locations
All runtime data lives in one directory: ~/.fengtang/ by default.
| File | Purpose | Permissions |
|---|---|---|
~/.fengtang/config.json |
Account credentials (email, auth code/token, server hosts, ports) | 0600 (owner-only, enforced on save) |
~/.fengtang/fengtang.db |
SQLite store: every fetched/sent message — raw RFC822 source, parsed headers/body, flags, attachments | 0600 (tightened after first run) |
Location resolution order (highest first):
data_dirfield insideconfig.json- Environment variable
FENGTANG_DATA_DIR - Default
~/.fengtang/
The mail database stores the full raw message plus parsed fields (subject, from/to, date, flags, text/html bodies), so searches work offline and attachments can be re-extracted any time. No message content is kept anywhere else — no code, no tests, no README ever contain real credentials or message data.
To move everything (config + mail) elsewhere:
export FENGTANG_DATA_DIR=/Volumes/SecureUSB/fengtang
Or set "data_dir": "/path" in the config file.
Configure an account (provider preset)
Configure an account (provider preset)
fengtang config add qq you@qq.com --password <SMTP-auth-code> --auth login --provider qq
fengtang config test -a qq # probes IMAP + POP3 + SMTP auth
Interactive OAuth login (Gmail / Outlook)
XOAUTH2 accounts (Gmail, Outlook) need no manual token hunting — one command runs the browser consent flow:
# Recommended: guided setup (opens the console, then continues into login)
fengtang setup-gmail you@gmail.com
# Or, with an existing client_id:
fengtang config login you@gmail.com --client-id <your-client-id>
fengtang config login -a <existing-account> # re-authorize an account
Google no longer permits shared public client_ids for the Gmail scope, so you create your own (Desktop app type — no verification review needed for your own account).
The provider's consent page opens in your default browser; a temporary local
loopback port catches the redirect, then access/refresh tokens are persisted
to ~/.fengtang/config.json (0600). An expired access token is refreshed
automatically with the stored refresh token on next use.
Fetch, list, read, search, mark
fengtang fetch -a qq -n 20 # same as before # pull new mail into local store
fengtang list --unread # newest first
fengtang search "Foxmail"
fengtang read 42 # headers + body
fengtang read 42 --save-attachments ./att
fengtang mark 42 --flags seen,flagged
fengtang folders
Send
fengtang send -t bob@example.com -s "Hello" -m "Body text" \
--attach ./report.pdf:report-2026.pdf
Run the built-in server
# Open (no auth) on localhost, storing into ~/.fengtang/fengtang.db
fengtang serve --smtp-port 2525 --pop-port 1110 --domain localhost
# Auth-protected mailboxes (repeat --user)
fengtang serve --user alice@localhost:secret1 --user bob@localhost:secret2
Then point any SMTP/POP3 client at 127.0.0.1:2525 / 127.0.0.1:1110. Local-domain delivery is accepted without auth; relaying to other domains requires authentication and is otherwise denied (550).
JSON output for scripting
Every command accepts --json and returns {"success", "data", "error", "metadata"}:
fengtang list --json --unread | jq '.data[0].subject'
Agent Integration (OpenAI Function Calling)
from fengtang.agent.tools import TOOLS, dispatch
# 1. Pass TOOLS to your model's tool list.
# 2. When the model calls a tool, route it:
result = dispatch("fengtang_send", {
"to": ["bob@example.com"],
"subject": "Hi from the agent",
"body": "Sent via fengtang tool call.",
})
print(result) # {"success": True, "data": {...}, "error": None, "metadata": {...}}
Available tools: fengtang_send, fengtang_list, fengtang_read, fengtang_search, fengtang_mark, fengtang_delete, fengtang_move, fengtang_fetch, fengtang_folders, fengtang_account_add, fengtang_account_list, fengtang_account_test.
Inspect the schema yourself: fengtang api --schema.
Python API
from fengtang import ToolResult, send_mail, list_messages, read_message, search_messages, mark_messages
result = send_mail(to=["bob@example.com"], subject="Hi", body="Hello")
if result.success:
print(result.data["message_id"])
All API functions return a ToolResult dataclass (success, data, error, metadata, .to_dict(), truthiness via __bool__).
Configuration
Accounts live in ~/.fengtang/config.json (0600). See Data & file locations above for the full layout and how to relocate it. Per-run overrides:
FENGTANG_DATA_DIR— data directoryfengtang serve --db <path>— custom database path for the built-in server
Project structure
fengtang/
├── core/ # config (accounts/presets), errors (ToolResult),
│ # auth (SASL PLAIN/LOGIN/CRAM-MD5/XOAUTH2/NTLM/APOP),
│ # _des + _md4 (pure-Python crypto primitives for NTLM)
├── mail/ # smtp_client, imap_client, pop_client,
│ # parser (MIME build/parse/render), store (SQLite)
├── serve/ # built-in asyncio SMTP + POP3 server
├── agent/ # OpenAI function-calling TOOLS + dispatch
└── cli/ # argparse CLI (config/send/fetch/list/read/search/…)
tests/ # pytest suite incl. end-to-end client↔built-in-server loops
Development
conda activate dev # your existing env; no venv needed
pip install -e .[dev]
pytest # 59 tests
ruff check . && ruff format .
mypy fengtang
Notes
- QQ/163/126 mailboxes use "authorization codes" (授权码) instead of the account password — pass it via
--password. Credentials are stored only in~/.fengtang/config.json(0600); they never appear in code, tests, or docs. - XOAUTH2 tokens can be supplied with
fengtang config test -a acct --oauth2-token <token>orAccount.oauth2_tokenfor Gmail/Outlook OAuth flows. - The built-in server stores delivered mail in the same SQLite store, so
fengtang fetch --protocol pop3round-trips against it — useful for testing agents without touching a real mailbox. - Development uses a conda env directly (e.g.
conda activate dev && pip install -e .[dev]) — no virtualenv required.
License
GPL-3.0-or-later (see LICENSE).
Release files for fengtang 0.0.7
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fengtang-0.0.7.tar.gz | 77.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fengtang-0.0.7-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 147.2 kB
Release files / fengtang-0.0.7.tar.gz
| Download URL | fengtang-0.0.7.tar.gz |
|---|---|
| Size | 77.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
09c1d4f0a320286e9f515019ec868c21fd70616f52affee6fa41a4a52560d4d7
|
|
BLAKE2b-256 checksum How to use checksums |
43f788eb758db1821b3019e535f5d6db14aede6df57e8cd90f0b638afab79e55
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.12
|
Release files / fengtang-0.0.7-py3-none-any.whl
| Download URL | fengtang-0.0.7-py3-none-any.whl |
|---|---|
| Size | 69.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f3c395dd2e0fb256fbf85926e75c81b70477d426c7ae71048004905c5cbd53b3
|
|
BLAKE2b-256 checksum How to use checksums |
05c88a9930c26bd60972f75d941caff2bc0555e35861a4977a2572b3d92c7e9d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.12
|