feat(email): 配置 foxmail SMTP 发信 + 发件人显示名品牌化 + bump 0.23.2

- .env 填入 smtp.qq.com:25/STARTTLS/授权码,send_email tool 与定时任务
  notify 兜底投递生效(.env 不入库)
- send_email.py 发件人显示名由硬编码 zcbot 改读 SMTP_FROM_NAME,默认
  「总院科研辅助智能体」,对外不暴露内部代号
- RUN.md 补 SMTP_FROM_NAME 说明;PROGRESS 记一条

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-06-24 11:31:17 +08:00
parent 340786a42f
commit 320f428dd3
4 changed files with 11 additions and 3 deletions

View File

@ -21,6 +21,11 @@
## 已完成关键能力 ## 已完成关键能力
### 2026-06-24 / 配置 QQ/foxmail SMTP 发信 + 发件人显示名品牌化(bump 0.23.2)
- `.env` 填入 foxmail SMTP(smtp.qq.com:25 / STARTTLS / 授权码),`send_email` tool 与定时任务 notify 兜底投递就此生效;自检发信链路通过。
- `tools/send_email.py` 发件人显示名从硬编码 `zcbot` 改为读 `SMTP_FROM_NAME`,默认「总院科研辅助智能体」—— 对外不暴露内部代号。RUN.md env 段补 `SMTP_FROM_NAME`
### 2026-06-24 / 微信任务徽章改品牌绿 + 微信 logo + 整行绿边(bump 0.23.1) ### 2026-06-24 / 微信任务徽章改品牌绿 + 微信 logo + 整行绿边(bump 0.23.1)
- 上一版徽章复用 `.badge.active`(蓝灰),与旁边「进行中」状态徽章撞色、不显眼。 - 上一版徽章复用 `.badge.active`(蓝灰),与旁边「进行中」状态徽章撞色、不显眼。

1
RUN.md
View File

@ -51,6 +51,7 @@
# SMTP_USER=you@qq.com # SMTP_USER=you@qq.com
# SMTP_PASSWORD=<授权码/应用专用密码,非登录密码> # SMTP_PASSWORD=<授权码/应用专用密码,非登录密码>
# SMTP_FROM=you@qq.com # 可选,默认取 SMTP_USER # SMTP_FROM=you@qq.com # 可选,默认取 SMTP_USER
# SMTP_FROM_NAME=总院科研辅助智能体 # 可选,发件人显示名,默"总院科研辅助智能体"(不暴露内部代号)
# 定时任务守护循环(DESIGN §8.5,随 web 进程起,plain-asyncio 仿 _disk_scanner): # 定时任务守护循环(DESIGN §8.5,随 web 进程起,plain-asyncio 仿 _disk_scanner):
# ZCBOT_DISABLE_SCHEDULER=1 # 可选,整体关掉调度(对照 Claude Code CLAUDE_CODE_DISABLE_CRON) # ZCBOT_DISABLE_SCHEDULER=1 # 可选,整体关掉调度(对照 Claude Code CLAUDE_CODE_DISABLE_CRON)
# ZCBOT_SCHEDULER_TICK_SECONDS=10 # 可选,扫描间隔,默 10s(只决定最坏延迟≤1tick,不影响会否漏) # ZCBOT_SCHEDULER_TICK_SECONDS=10 # 可选,扫描间隔,默 10s(只决定最坏延迟≤1tick,不影响会否漏)

View File

@ -1,3 +1,3 @@
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
# 改版本只动这一行。 # 改版本只动这一行。
__version__ = "0.23.1" __version__ = "0.23.2"

View File

@ -8,7 +8,8 @@
密钥只在 host 进程读,绝不进沙箱 / run_pythonenv: 密钥只在 host 进程读,绝不进沙箱 / run_pythonenv:
SMTP_HOST SMTP_PORT( 465) SMTP_USER SMTP_PASSWORD SMTP_HOST SMTP_PORT( 465) SMTP_USER SMTP_PASSWORD
SMTP_FROM( SMTP_USER) SMTP_TLS(ssl|starttls|none;默按端口:465ssl 否则 starttls) SMTP_FROM( SMTP_USER) SMTP_FROM_NAME(发件人显示名,"总院科研辅助智能体")
SMTP_TLS(ssl|starttls|none;默按端口:465ssl 否则 starttls)
""" """
from __future__ import annotations from __future__ import annotations
@ -62,6 +63,7 @@ def send_email_smtp(
user = os.getenv("SMTP_USER", "").strip() user = os.getenv("SMTP_USER", "").strip()
password = os.getenv("SMTP_PASSWORD", "").strip() password = os.getenv("SMTP_PASSWORD", "").strip()
sender = os.getenv("SMTP_FROM", "").strip() or user sender = os.getenv("SMTP_FROM", "").strip() or user
from_name = os.getenv("SMTP_FROM_NAME", "").strip() or "总院科研辅助智能体"
if isinstance(to, str): if isinstance(to, str):
to_list = [to] to_list = [to]
@ -74,7 +76,7 @@ def send_email_smtp(
raise ValueError(f"收件人过多(上限 {_MAX_RECIPIENTS})") raise ValueError(f"收件人过多(上限 {_MAX_RECIPIENTS})")
msg = EmailMessage() msg = EmailMessage()
msg["From"] = formataddr(("zcbot", sender)) msg["From"] = formataddr((from_name, sender))
msg["To"] = ", ".join(to_list) msg["To"] = ", ".join(to_list)
msg["Subject"] = subject or "(无主题)" msg["Subject"] = subject or "(无主题)"
msg.set_content(body or "") msg.set_content(body or "")