Skip to main content

FengTang(冯唐)

English | 中文

About the name: FengTang (冯唐) is taken from a tale in the Records of the Grand Historian (《史记·张释之冯唐列传》). Under Emperor Wen of Han, the governor of Yunzhong, Wei Shang, was stripped of rank over a minor miscount in reporting enemy heads. Feng Tang spoke up — “your rewards are too light and your punishments too heavy” — and the emperor, moved, that very day sent Feng Tang bearing the imperial tally (持节) to Yunzhong to deliver the edict of pardon (赦书), restoring Wei Shang to office. Su Shi's celebrated line — “bearing the tally to Yunzhong; when will Feng Tang be sent?” (持节云中,何日遣冯唐) — alludes to exactly this. Hence the name: may every message be carried as faithfully as that pardon, and arrive exactly as it must.

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 POP3 APOP. auto mode 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 / MTA: a self-contained SMTP + POP3 server (asyncio, zero system dependencies) that sends (direct-to-MX with its own pure-Python DNS resolver, or smarthost relay) and receives (local delivery plus external-mailbox pull) real mail — no external tools.
  • 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 TOOLS schema + dispatch() for every operation, so an LLM agent can read/write/search/mark mail exactly like a human user.

Installation

Requires Python ≥ 3.10. Zero runtime dependencies — pure standard library. There is no venv requirement and no system dependency: no OpenSSL bindings, no external mail binaries, no Node/Go/Rust toolchain.

Use whatever Python environment you already have; pick one:

# 1. PyPI, into the current environment
pip install fengtang

# 2. Isolated CLI install, no environment juggling (recommended for CLI use)
pipx install fengtang

# 3. conda — create your own env; the name is entirely up to you
conda create -n mymail python=3.12
conda activate mymail
pip install fengtang

# 4. From source (development; [dev] only adds pytest / ruff / mypy)
git clone https://github.com/cycleuser/FengTang && cd FengTang
pip install -e ".[dev]"

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):

  1. data_dir field inside config.json
  2. Environment variable FENGTANG_DATA_DIR
  3. 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.

Credentials & security (important)

Passwords live only in the local config — never in code, docs, tests, or git.

Credentials are stored exclusively in ~/.fengtang/config.json (mode 0600). Three out-of-band channels are supported, in priority order:

  1. Environment variable (highest priority, nothing written to disk)
    export FENGTANG_PASSWORD_QQ=<your-auth-code>   # name = FENGTANG_PASSWORD_ + ACCOUNT (upper)
    fengtang fetch -a qq
    
  2. Password file (secret kept in its own file, e.g. an encrypted volume)
    fengtang config add qq you@qq.com --password-file ~/.secrets/qq.txt
    
  3. Secure set (no echo, no shell history, no chat transcript)
    fengtang config set-password qq            # interactive, confirmed twice
    echo "$PW" | fengtang config set-password qq --stdin   # pipe from a password manager
    fengtang config set-password qq --password-file ~/.secrets/qq.txt
    

fengtang config add without --password prompts with no echo. Prefer these interactive/file/env forms so a secret never lands in a plaintext command line or a pasted conversation.

Move accounts to another machine

Only the account configuration is exported — never the message database.

# Export (passphrase-protected; prompts with no echo)
fengtang config export ~/Desktop/fengtang-accounts.fgbundle

# Export without a passphrase (plaintext — delete after use)
fengtang config export ~/Desktop/fengtang-accounts.fgbundle --plaintext

# On the other machine
fengtang config import ~/Desktop/fengtang-accounts.fgbundle
fengtang config import <file> --replace      # replace all instead of merging by name

The bundle is plain JSON and is a superset of config.json, so on a machine without this command you can simply copy it into place:

mkdir -p ~/.fengtang && cp fengtang-accounts.fgbundle ~/.fengtang/config.json

Encrypted bundles use PBKDF2-HMAC-SHA256 (200k iterations) + HMAC-SHA256 keystream + encrypt-then-MAC, implemented with the standard library only.

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 mail server (a self-contained MTA)

The built-in server both receives and sends real mail with no external tools — its own DNS resolver, its own SMTP client, its own SMTP/POP3 servers.

# Local-only server: receives into ~/.fengtang/fengtang.db
fengtang serve --smtp-port 2525 --pop-port 1110 --domain localhost

# Auth-protected local mailboxes (repeat --user)
fengtang serve --user alice@localhost:secret1 --user bob@localhost:secret2

# Full send+receive: outbound via a configured account (smarthost) and
# inbound aggregation from real external mailboxes
fengtang serve --domain fengtang.local \
  --user "me@fengtang.local:localpw" \
  --relay-account qq --outbound auto \
  --pull foxmail --pull-interval 60

Outbound (send): for any non-local recipient, the server delivers the message itself:

  • --outbound direct — resolve the recipient's MX with the built-in pure-Python DNS client and speak SMTP on port 25 (no auth, no relay);
  • --outbound smarthost — hand it to an authenticated upstream account named by --relay-account (like Postfix's relayhost); the From is rewritten to that account and the original is kept in Reply-To / X-Original-From;
  • --outbound auto (default) — try direct-to-MX, fall back to the smarthost.

Inbound (receive): the SMTP listener accepts mail for local domains and stores it; --pull ACCOUNT[:FOLDER] additionally polls real external mailboxes (IMAP or POP3) and aggregates them into the local store — so a server without a public IP or its own MX can still receive. --pull-interval controls the poll period (0 runs one pull at startup).

Relaying to non-local domains always requires authentication — the server is never an open relay. Point any SMTP/POP3 client at 127.0.0.1:2525 / 127.0.0.1:1110.

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 directory
  • fengtang 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 MTA: asyncio SMTP + POP3 server, pure-Python
│                    # DNS resolver (dns.py), outbound delivery (outbound.py),
│                    # external-mailbox pull (pull.py)
├── 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

Any Python ≥ 3.10 environment. From a source checkout:

pip install -e ".[dev]"   # adds pytest / ruff / mypy
pytest                    # 59 tests
ruff check . && ruff format .
mypy fengtang

Acknowledgments & prior art

FengTang builds on decades of protocol work, and for the trickiest part — modern OAuth2 against Gmail and Outlook — it deliberately follows Mozilla Thunderbird's implementation rather than guessing at provider rules:

  • OAuth2 provider data (fengtang/mail/oauth.py, BUILTIN_PROVIDERS) mirrors Thunderbird's mailnews/base/src/OAuth2Providers.sys.mjs: the built-in public client IDs (Google 406964657835-…, Microsoft 9e5f94bc-…), the authorization and token endpoints, the exact mail scopes (Google https://mail.google.com/; Microsoft outlook.office.com/IMAP.AccessAsUser.All, POP.AccessAsUser.All, SMTP.Send, offline_access), and which providers require PKCE.
  • The browser OAuth flow (interactive_login) mirrors Thunderbird's mailnews/base/src/OAuth2.sys.mjs: a loopback listener on 127.0.0.1 / localhost bound to a randomly chosen port (the providers' registered loopback redirects permit any port), a local state check, and the authorization-code exchange including code_verifier and client_secret where the provider requires them.
  • Thunderbird is MPL-2.0. The values reused here are factual protocol/registration data; all code in FengTang is an independent Python implementation.

Other references: RFC 5321 (SMTP), RFC 3501 (IMAP4rev1), RFC 1939 (POP3), RFC 4616 (SASL PLAIN), RFC 2195 (CRAM-MD5), RFC 7628 (OAUTHBEARER), RFC 7636 (PKCE), RFC 2595 (STARTTLS), and Microsoft's MS-NLMP.

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> or Account.oauth2_token for Gmail/Outlook OAuth flows.
  • The built-in server stores delivered mail in the same SQLite store, so fengtang fetch --protocol pop3 round-trips against it — useful for testing agents without touching a real mailbox.

License

GPL-3.0-or-later (see LICENSE).


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——一个参数填好全部服务器与端口。
  • 内置邮件服务器 / MTA:自带的 asyncio SMTP + POP3 服务端(零系统依赖),既能发(自带纯 Python DNS 解析器直投 MX,或经智能主机转发)也能收(本地投递 + 外部邮箱归集)真实邮件——不依赖任何外部工具。
  • 本地 SQLite 存储:每封收取/发出的邮件都可离线搜索、标记、移动、删除。
  • MIME 处理完善:multipart/alternative、RFC 2047 中文头、附件(列出 + 保存)、HTML→纯文本兜底。
  • 智能体 API:每个操作都有对应的 OpenAI function-calling TOOLS schema + dispatch(),LLM 智能体能像人一样收发、搜索、标记邮件。

安装

要求 Python ≥ 3.10,运行时零第三方依赖——全部标准库。 不需要 venv,也没有任何系统依赖:不需要 OpenSSL 绑定、不需要外部邮件程序、 不需要 Node/Go/Rust 工具链。

用你已有的任意 Python 环境即可,任选一种:

# 1. 从 PyPI 装进当前环境
pip install fengtang

# 2. 隔离式命令行安装,免去管环境(pipx,推荐命令行用户)
pipx install fengtang

# 3. conda —— 自建环境,名字随你起
conda create -n mymail python=3.12
conda activate mymail
pip install fengtang

# 4. 从源码(开发用;[dev] 只额外装 pytest / ruff / mypy)
git clone https://github.com/cycleuser/FengTang && cd FengTang
pip install -e ".[dev]"

数据与文件位置

所有运行时数据集中在一个目录:默认 ~/.fengtang/。

文件 用途 权限
~/.fengtang/config.json 账号凭据(邮箱、授权码/token、服务器地址、端口) 0600(保存时强制,仅属主可读写)
~/.fengtang/fengtang.db SQLite 存储:每封收取/发出的邮件——原始 RFC822 报文、解析后的头/正文、标志、附件 0600(首次运行后收紧)

目录解析优先级(从高到低):

  1. config.json 内的 data_dir 字段
  2. 环境变量 FENGTANG_DATA_DIR
  3. 默认 ~/.fengtang/

邮件数据库同时保存完整原始报文与解析后的字段(主题、发收件人、日期、标志、text/html 正文),因此可离线搜索,附件可随时重新提取。任何真实凭据或邮件内容都不会出现在代码、测试或文档中。

整体迁移(config + 邮件):

export FENGTANG_DATA_DIR=/Volumes/SecureUSB/fengtang

或在配置文件里设 "data_dir": "/path"。

凭据安全(重要)

所有密码只存本地,绝不进入代码、文档、测试或版本库。

凭据唯一位于 ~/.fengtang/config.json(权限 0600,仅属主可读)。此外提供三种"不落盘/不明文"通道,按优先级:

  1. 环境变量(优先级最高,完全不写入磁盘)
    export FENGTANG_PASSWORD_QQ=<你的授权码>   # 变量名 = FENGTANG_PASSWORD_ + 账号名大写
    fengtang fetch -a qq
    
  2. 密码文件(敏感度落在单独文件,可放加密盘)
    fengtang config add qq you@qq.com --password-file ~/.secrets/qq.txt
    
  3. 安全设置密码(终端不回显,不进 shell history,也不进对话记录)
    fengtang config set-password qq            # 交互式,两次输入确认
    echo "$PW" | fengtang config set-password qq --stdin   # 从密码管理器管道输入
    fengtang config set-password qq --password-file ~/.secrets/qq.txt
    

fengtang config add 若不带 --password,也会用不回显的方式提示输入。请始终优先用上述交互/文件/环境变量方式,避免把密码写进命令行明文或粘贴到任何对话窗口。

把账号搬到另一台机器

只导出账号配置,绝不包含邮件数据库。

# 导出(带口令保护;提示输入时不回显)
fengtang config export ~/Desktop/fengtang-accounts.fgbundle

# 不带口令导出(明文——用完请删)
fengtang config export ~/Desktop/fengtang-accounts.fgbundle --plaintext

# 在另一台机器上导入
fengtang config import ~/Desktop/fengtang-accounts.fgbundle
fengtang config import <文件> --replace      # 覆盖全部,而非按账号名合并

bundle 是纯 JSON,且是 config.json 的超集,所以在没有该命令的机器上也可以 直接复制到位:

mkdir -p ~/.fengtang && cp fengtang-accounts.fgbundle ~/.fengtang/config.json

加密版本用标准库实现:PBKDF2-HMAC-SHA256(20 万次迭代)+ HMAC-SHA256 密钥流

  • 先加密后 MAC。

配置账号(服务商预设)

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

运行内置邮件服务器(自带完整 MTA)

内置服务器收、发真实邮件,全程不依赖任何外部工具——自带 DNS 解析器、自带 SMTP 客户端、自带 SMTP/POP3 服务端。

# 纯本地:收信存入 ~/.fengtang/fengtang.db
fengtang serve --smtp-port 2525 --pop-port 1110 --domain localhost

# 带认证的本地邮箱(--user 可重复)
fengtang serve --user alice@localhost:secret1 --user bob@localhost:secret2

# 完整收发:经已配置账号(智能主机)发信 + 从真实外部邮箱归集收信
fengtang serve --domain fengtang.local \
  --user "me@fengtang.local:localpw" \
  --relay-account qq --outbound auto \
  --pull foxmail --pull-interval 60

发信(outbound): 凡非本地收件人,服务器自行投递:

  • --outbound direct —— 用内置纯 Python DNS 客户端查出对方 MX,直接走 25 端口 投递(不认证、不经中转);
  • --outbound smarthost —— 交给 --relay-account 指定的、已认证的上游账号转发 (相当于 Postfix 的 relayhost);From 改写为该账号,原发件人保留在 Reply-To / X-Original-From;
  • --outbound auto(默认)—— 先直投 MX,失败再走智能主机。

收信(inbound): SMTP 监听端口接收本地域邮件并入库;--pull ACCOUNT[:FOLDER] 则额外轮询真实外部邮箱(IMAP 或 POP3),归集进本地库——这样即便服务器没有公网 IP 或自己的 MX,也能收到信。--pull-interval 控制轮询周期(0 表示仅启动时拉取一次)。

向非本地域中继始终需要认证——服务器绝不会变成开放中继。任意 SMTP/POP3 客户端 连 127.0.0.1:2525 / 127.0.0.1:1110 即可。

脚本化 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/           # 内置 MTA:asyncio SMTP+POP3 服务端、纯 Python DNS 解析器
│                    # (dns.py)、外发投递(outbound.py)、外部邮箱归集(pull.py)
├── agent/           # OpenAI function-calling TOOLS + dispatch
└── cli/             # argparse CLI(config/send/fetch/list/read/search/…)
tests/               # pytest 套件,含客户端↔内置服务器端到端回环

开发

任意 Python ≥ 3.10 环境。在源码目录下:

pip install -e ".[dev]"   # 额外装 pytest / ruff / mypy
pytest                    # 59 个测试
ruff check . && ruff format .
mypy fengtang

致谢与参考实现

FengTang 建立在大量既有协议工作之上;其中最棘手的一块——面向 Gmail/Outlook 的 现代 OAuth2——是刻意参照 Mozilla Thunderbird 的实现来做的,而不是自己臆测各家规则:

  • OAuth2 服务商数据(fengtang/mail/oauth.py 的 BUILTIN_PROVIDERS)对齐 Thunderbird 的 mailnews/base/src/OAuth2Providers.sys.mjs:内置公共 client_id (Google 406964657835-…、Microsoft 9e5f94bc-…)、授权与 token 端点、精确的 邮件 scope(Google https://mail.google.com/;Microsoft outlook.office.com/IMAP.AccessAsUser.All、POP.AccessAsUser.All、 SMTP.Send、offline_access),以及哪些服务商要求 PKCE。
  • 浏览器 OAuth 流程(interactive_login)对齐 Thunderbird 的 mailnews/base/src/OAuth2.sys.mjs:在 127.0.0.1/localhost 上用一个随机端口 起回环监听(各家注册的回环跳转都允许任意端口)、本地 state 校验,以及 授权码兑换(按服务商要求在请求中带上 code_verifier 与 client_secret)。
  • Thunderbird 采用 MPL-2.0 许可。此处复用的是事实性的协议/注册数据,本项目的所有 代码均为独立的 Python 实现。

其他参考:RFC 5321(SMTP)、RFC 3501(IMAP4rev1)、RFC 1939(POP3)、 RFC 4616(SASL PLAIN)、RFC 2195(CRAM-MD5)、RFC 7628(OAUTHBEARER)、RFC 7636 (PKCE)、RFC 2595(STARTTLS),以及微软的 MS-NLMP。

备注

  • 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 可以对它完整回环——测试智能体时不必碰真实邮箱。

许可

GPL-3.0-or-later(见 LICENSE)。

Release files for fengtang 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fengtang 0.1.1
File Size Uploaded
fengtang-0.1.1.tar.gz 104.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fengtang 0.1.1
File Interpreter ABI Platform
fengtang-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 190.0 kB

Release files / fengtang-0.1.1.tar.gz

Download URL fengtang-0.1.1.tar.gz
Size 104.4 kB
Tags Source
SHA-256 checksum
How to use checksums
62ef0ef7216f6ce4e19a80822555b3a3e810fe68c1e0494ddec0476304c330fd
BLAKE2b-256 checksum
How to use checksums
8348b467c253a5dfbb774ebab7e8d097d37b9a5f29cc097dcb5c05714d084de9
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.1.1-py3-none-any.whl

Download URL fengtang-0.1.1-py3-none-any.whl
Size 85.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4425fe970b8452bb6fe1974e123ef3810eb9a3c6c4412ef90b095c1b47201417
BLAKE2b-256 checksum
How to use checksums
996896e1441fdbf812edc1697eb8158e21d63178c6675a1ab2ded70798f7b273
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.12

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page