en un clic
weekly-report-email
从飞书表格读取周报数据(JAVA组),汇总整理后通过邮件发送
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Menu
从飞书表格读取周报数据(JAVA组),汇总整理后通过邮件发送
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Basé sur la classification professionnelle SOC
Clone/create/fork repos; manage remotes, releases.
Run LLM inference with llama.cpp on CPU, Apple Silicon, AMD/Intel GPUs, or NVIDIA — plus GGUF model conversion and quantization (2–8 bit with K-quants and imatrix). Covers CLI, Python bindings, OpenAI-compatible server, and Ollama/LM Studio integration. Use for edge deployment, M1/M2/M3/M4 Macs, CUDA-less environments, or flexible local quantization.
Automates the generation and delivery of a daily news briefing covering finance, military, and technology sectors.
Gmail, Calendar, Drive, Contacts, Sheets, and Docs integration for Hermes. Uses Hermes-managed OAuth2 setup, prefers the Google Workspace CLI (`gws`) when available for broader API coverage, and falls back to the Python client libraries otherwise.
Manage Linear issues, projects, and teams via the GraphQL API. Create, update, search, and organize issues. Uses API key auth (no OAuth needed). All operations via curl — no dependencies.
搜索、提取当日财经/军事/科技新闻,格式化后通过飞书推送
| name | weekly-report-email |
| description | 从飞书表格读取周报数据(JAVA组),汇总整理后通过邮件发送 |
| triggers | ["汇总周报","发送周报邮件","周报发送"] |
从飞书电子表格读取周报数据(JAVA组),按表格格式汇总整理,并通过邮件发送。
用户说:「汇总周报」或类似表达
| 项目 | 值 |
|---|---|
| Spreadsheet Token | KYghscfQth2isytvD39c7FQUn3c |
| App ID | cli_YOUR_APP_ID |
| App Secret | ziZAkavTw4sa7DHJCblBuewhNpUFpwv2 |
| 目标群 chat_id | oc_YOUR_CHAT_ID(助手群) |
| JAVA组成员 | 肖渊、郭能清、祝小娟、袁钞、高雷、何灿、何永辉、徐彬辉 |
⚠️ 凭证适用范围:这些 Feishu API 凭证(App ID / App Secret)不仅用于 Sheets API 读取,也可用于
im/v1/messages发送消息、搜索聊天等。不同操作可能需要不同的 API scope,确保 Bot 已开通对应权限。
| App Secret | ziZAkavTw4sa7DHJCblBuewhNpUFpwv2 |
⚠️ 凭证同步规则(重要):App Secret 变更时必须同步更新:
/root/.hermes/cron/send_weekly_report.py第12行app_secret = '...'- Hermes memory 中的 App Secret 条目
飞书消息(Bot/Gateway)与 API 调用(Sheets/文档)是两套独立认证:
- Bot 聊天正常 ≠ API 凭证有效
- API 报 401 时请先验证凭证:
POST https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal
用户说「读取sheet XXX」或「读取XXX」,直接使用指定sheet ID。
自动获取所有sheet列表,按title日期排序,取最新且hidden=false的sheet_id。
| 列索引 | 列名 |
|---|---|
| 0 | 成员 |
| 1 | 本周参与项目 |
| 2 | 本周关键进展(工作内容及进度百分比) |
| 3 | 协助、风险及应对措施 |
| 4 | 下周参与项目 |
| 5 | 下周重点工作(工作内容及进度百分比) |
项目分类统一从sheet mhopvH 读取。
category_map[str(row[1]).strip()] = str(row[0]).strip()
POST https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal
Body: {"app_id": "cli_YOUR_APP_ID", "app_secret": "<APP_SECRET>"}
GET https://open.feishu.cn/open-apis/sheets/v3/spreadsheets/KYghscfQth2isytvD39c7FQUn3c/sheets/query
GET https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/KYghscfQth2isytvD39c7FQUn3c/values/{sheet_id}
默认格式:JAVA组 周工作总结 - 日期(如 JAVA组 周工作总结 - 2026-05-29)
重要:用户每次可能指定不同主题,以用户当次指令为准。
| 分类 | 重点工作 | 具体事项及进展 | 参与人 | 协助/风险 |
|---|
| 分类 | 重点工作 | 具体事项及计划进展 | 相关人 |
|---|
<!-- 表头 -->
<tr style="background-color: #4CAF50; color: white;">
<th style="width:70px; white-space:nowrap;">分类</th>
<th style="width:140px; white-space:nowrap;">重点工作</th>
<th>具体事项及进展</th>
<th style="width:100px; white-space:nowrap;">参与人</th>
<th style="width:150px;">协助/风险</th>
</tr>
<!-- 数据行 -->
<td style="white-space:nowrap;">分类</td>
<td style="white-space:nowrap;">重点工作</td>
<td style="word-wrap:break-word; white-space:normal;">具体事项及进展内容...</td>
<td style="white-space:nowrap;">参与人</td>
<td style="word-wrap:break-word; white-space:normal;">协助/风险内容...</td>
解析时按项目聚合数据:
this_week = defaultdict(lambda: {'members': set(), 'content': [], 'assist_risk': []})
next_week = defaultdict(lambda: {'members': set(), 'content': []})
smart_summarize() 智能汇总\n 拆分成独立的行1、2、3、... 格式输出summarize_by_person() 按成员分别描述<br>换行何灿:配置前端部署环境;编写jenkins构建脚本
郭能清:AI工具平台已完成第一个版本开发部署到测试环境
this_week[proj]['assist_risk'].append(assist)<br>合并:'<br>'.join(data['assist_risk'])基建/业务支持/运维分类必须按 (项目, 成员) 组合直接分组,不能在内容中查找人名!
原因:原始数据每行已记录成员姓名(列0),但进展内容(列2)中不一定包含成员姓名。
# 正确代码示例
this_week = defaultdict(lambda: defaultdict(lambda: {'content': [], 'assist_risk': []}))
for row in sheet_rows[1:]:
member = str(row[0]).strip() # 直接从列0获取成员名
proj_this = str(row[1]).strip()
content_this = str(row[2]).strip()
# ...
this_week[proj_this][member]['content'].append(content_this)
原始数据中包含换行符(如 1、xxx\n2、xxx),需要转换为 <br>,但要避免产生空白行:
re.sub(r'\n{2,}', '\n', content)content.strip()<br>:content.replace('\n', '<br>')风险内容从列3读取,非空且非"暂无"才记录。原始数据中每行的风险内容需要正确归属到对应项目。
| 项目 | 值 |
|---|---|
| 发件人/默认收件人 | hexu@songtsam.com |
| 可追加收件人 | digital-center@songtsam.com(用户曾要求确认内容无误后将同一份周报追加发送到该邮箱;是否作为默认收件人需用户明确确认) |
| SMTP | smtp.songtsam.com:465 (SSL/TLS 1.2) |
用户明确要求:发送邮件前必须先展示预览内容,确认后再发送。
如果存在cron脚本 /root/.hermes/cron/send_weekly_report.py,每次修改凭证后必须同步更新该脚本:
app_secret 是否为最新值app_id 是否正确cron触发时执行的是脚本文件,不是技能文档。即使 skill 更新了,脚本里写死的旧凭证仍然会导致失败。
def format_content(content):
"""换行符转<br>,压缩连续空白行"""
content = content.replace('\r\n', '\n').replace('\r', '\n')
content = re.sub(r'\n{2,}', '\n', content) # 压缩连续空行
content = content.strip()
content = content.replace('\n', '<br>')
return content
def smart_summarize(items):
"""智能汇总:按换行拆分、去行首序号、聚类合并、提取进度、统一连贯序号"""
if not items:
return ''
# 第一步:按换行拆分成独立行
all_lines = []
for item in items:
item = item.strip().rstrip(';').rstrip(';').rstrip(';')
item = item.replace('\r\n', '\n').replace('\r', '\n')
lines = item.split('\n')
for line in lines:
line = line.strip()
if line:
# 只去掉行首的序号(如 "1、" 或 "1. "),保留行内其他内容
line = re.sub(r'^(\d+[.、]\s*)+', '', line)
if line:
all_lines.append(line)
if not all_lines:
return ''
# 第二步:按描述内容聚类合并
clusters = {}
for line in all_lines:
progress_match = re.search(r'进度(\d+)%', line)
progress = int(progress_match.group(1)) if progress_match else None
base = re.sub(r',进度\d+%|,进度\d+', '', line).strip()
if not base:
continue
found = False
for key in clusters:
if len(base) > 5 and len(key) > 5:
common = sum(1 for a, b in zip(base, key) if a == b)
if common >= max(len(base), len(key)) * 0.7:
if progress is not None:
existing = clusters[key]['progress']
clusters[key]['progress'] = max(existing, progress) if existing else progress
clusters[key]['items'].append(line)
found = True
break
if not found:
clusters[base] = {'items': [line], 'progress': progress}
# 第三步:构建结果,统一用连贯序号 1、2、3...
result = []
for i, (base, data) in enumerate(clusters.items(), 1):
if data['progress'] is not None:
result.append(f"{i}、{base},进度{data['progress']}%")
else:
result.append(f"{i}、{base}")
return '<br>'.join(result)
<br>换行