GenMail 生成邮件摘要并推送至群聊的脚本实现

文章导读
这个需求可以拆成三步:先安全地拿到待处理的邮件列表,再调用 GenMail 的摘要生成能力,最后把摘要 POST 到群聊 Webhook。由于不同邮件服务和群聊机器人的接口存在差异,下面给出的脚本骨架使用占位端点和通用 REST 风格,你需要根据实际接口替换域名、路径、请求头和参数。
📋 目录
  1. A 搭建读取邮件列表的请求骨架
  2. B 调用摘要生成接口并处理响应
  3. C 构造群聊 Webhook 发送程序
  4. D 运行端到端测试与日志审计
A A

这个需求可以拆成三步:先安全地拿到待处理的邮件列表,再调用 GenMail 的摘要生成能力,最后把摘要 POST 到群聊 Webhook。由于不同邮件服务和群聊机器人的接口存在差异,下面给出的脚本骨架使用占位端点和通用 REST 风格,你需要根据实际接口替换域名、路径、请求头和参数。

本方案适用于具备 REST API 的邮件服务和支持自定义机器人的群聊工具。操作上先拉取未读邮件列表,再调用摘要接口生成短文本,最后通过 Webhook 推送到群聊。验证方式是通过脚本日志查看每步请求的 URL、状态码和响应结构。风险在于接口鉴权、限流和消息格式因服务而异,需要先结合环境确认。

搭建读取邮件列表的请求骨架

获取邮件列表是整条链路的第一步。邮件服务一般提供 REST API,通常需要 OAuth2 或 Bearer Token 鉴权。为了不把凭据写死在代码里,建议通过环境变量读取。下面是用 requests 库实现的分页拉取骨架:

import os
import requests

BASE_URL = os.getenv("MAIL_API_BASE_URL", "https://mail.example.com/api/v1")
TOKEN = os.getenv("MAIL_API_TOKEN", "replace-with-real-token")

headers = {
    "Authorization": f"Bearer {TOKEN}",
    "Accept": "application/json"
}

def fetch_unread_emails(page_size=20):
    params = {
        "query": "is:unread",
        "limit": page_size,
        "page_token": ""
    }
    emails = []
    while True:
        resp = requests.get(
            f"{BASE_URL}/emails",
            headers=headers,
            params=params,
            timeout=10
        )
        resp.raise_for_status()
        data = resp.json()
        emails.extend(data.get("items", []))
        next_token = data.get("next_page_token")
        if not next_token:
            break
        params["page_token"] = next_token
    return emails

这段代码先按未读条件请求第一页,再根据返回的 next_page_token 继续翻页,直到拉完所有未读邮件。这里的 base_url、分页参数名和未读过滤字段都需要替换成实际邮件 API 的类型。如果邮件服务只支持日期范围或文件夹 ID,就把 params 里的 query 改为对应字段。运行前先单独测试 fetch_unread_emails,确认返回的 items 里包含邮件 ID 和正文或正文链接。

调用摘要生成接口并处理响应

拿到邮件列表后,把邮件 ID 或正文文本传给摘要接口。GenMail 这类服务的接口通常接收一个文本列表,返回每封邮件的 summary。发送时建议加超时和重试,避免网络抖动导致任务中断。下面是一个 POST 请求示例:

def generate_summary(email_ids, email_contents):
    summarize_url = os.getenv("SUMMARY_API_URL", "https://genmail.example.com/v1/summarize")
    payload = {
        "email_ids": email_ids,
        "contents": email_contents,
        "max_length": 120
    }
    for attempt in range(3):
        try:
            resp = requests.post(
                summarize_url,
                json=payload,
                headers=headers,
                timeout=30
            )
            resp.raise_for_status()
            data = resp.json()
            return data.get("summaries", [])
        except (requests.Timeout, requests.RequestException) as e:
            if attempt == 2:
                raise e
            time.sleep(2 * (attempt + 1))

注意几个细节:responses.json() 里若是包含 summaries 列表,就按 email_id 和 summary 字段组装;如果摘要接口直接返回 Markdown 或纯文本,需要先解析再拿内容。超时时间要根据邮件正文大小调整,正文很大时 30 秒可能不够。重试逻辑里也建议记录 attempt 次数,方便日志审计。

GenMail 生成邮件摘要并推送至群聊的脚本实现

构造群聊 Webhook 发送程序

群聊推送目前最常见的三种方式:Slack Incoming Webhook、钉钉自定义机器人、企业微信机器人。它们的请求体结构不同,但核心都是 POST JSON。下面封装了一个通用函数,用 webhook_type 区分格式:

def send_webhook(webhook_url, webhook_type, title, text):
    if webhook_type == "slack":
        payload = {"text": f"*{title}*\n{text}"}
    elif webhook_type == "dingtalk":
        payload = {"msgtype": "text", "text": {"content": f"{title}\n{text}"}}
    elif webhook_type == "wecom":
        payload = {"msgtype": "text", "text": {"content": f"{title}\n{text}"}}
    else:
        raise ValueError(f"Unsupported webhook_type: {webhook_type}")

    resp = requests.post(webhook_url, json=payload, timeout=10)
    resp.raise_for_status()
    return resp.json()

消息模板建议包含邮件发件人、主题、摘要和原始链接。钉钉和企业微信机器人通常需要加关键字或签名,如果推送不成功,先检查安全设置是否匹配。Slack 可以直接显示 Markdown,但钉钉和企业微信只支持部分格式,所以模板里统一用普通文本加换行。

运行端到端测试与日志审计

把以上函数串成完整流程后,需要确保每一步都有日志。下面是一个测试主函数,打印每个阶段的关键信息:

import logging
logging.basicConfig(level=logging.INFO)

def main():
    emails = fetch_unread_emails()
    logging.info("Fetched %d emails", len(emails))
    email_ids = [e["id"] for e in emails]
    contents = [e.get("snippet") or e.get("body") for e in emails]

    summaries = generate_summary(email_ids, contents)
    logging.info("Summaries received: %s", len(summaries))

    for item in summaries:
        email_id = item["email_id"]
        summary = item["summary"]
        title = f"邮件摘要 {email_id}"
        send_webhook(os.getenv("GROUP_WEBHOOK_URL"), "dingtalk", title, summary)
        logging.info("Sent summary for %s to webhook", email_id)

if __name__ == "__main__":
    main()

日志里要记录请求的 URL、状态码和响应摘要。可以在每个请求后加一条类似 logging.info("POST %s -> %s", url, resp.status_code) 的输出,但上面为了简洁只写了阶段日志。建议在开发时先用一个测试群聊或临时 webhook 跑一次,确认消息能送达。常见异常包括:401 鉴权失败、404 路径错误、429 限流、以及 JSON 解析错误。碰到 429 时退避重试,碰到 401 时检查 token 是否过期。最后,整个脚本可以挂到 cron 或 CI 定时任务中,但要注意邮件服务只读取不标记已读,否则会重复收到同一批邮件。